From 2bcfa96ad8b42f2d02d7441f657c96a0c7fcf3bf Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 16 May 2012 00:50:06 +0400 Subject: [PATCH] Fix highlighting performance tests; show a difference if failing --- .../advHighlighting/aClassLoader_hl.java | 1731 ++++++ .../advHighlighting/aThinlet_hl.java | 5393 +++++++++++++++++ .../LightAdvHighlightingPerformanceTest.java | 57 +- .../daemon/LightDaemonAnalyzerTestCase.java | 2 +- 4 files changed, 7156 insertions(+), 27 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/aClassLoader_hl.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/aThinlet_hl.java diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/aClassLoader_hl.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/aClassLoader_hl.java new file mode 100644 index 000000000000..c7f959b6a630 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/aClassLoader_hl.java @@ -0,0 +1,1731 @@ +/* + * @(#)ClassLoader.java 1.162 02/03/19 + * + * Copyright 2002 Sun Microsystems, Inc. All rights reserved. + * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + +import java.io.InputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.io.PrintWriter; +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Hashtable; +import java.util.Enumeration; +import java.util.Vector; +import java.util.Stack; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.jar.Manifest; +import java.net.URL; +import java.net.MalformedURLException; +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; +import java.security.Permissions; +import java.security.CodeSource; +import java.security.Policy; +import sun.misc.URLClassPath; +import sun.misc.Resource; +import sun.misc.CompoundEnumeration; +import sun.misc.ClassFileTransformer; +//import sun.misc.Launcher; +import sun.reflect.Reflection; +import sun.security.action.GetPropertyAction; + +/** + * A class loader is an object that is responsible for loading + * classes. The class ClassLoader is an abstract class. + * Given the name of a class, a class loader should attempt to locate + * or generate data that constitutes a definition for the class. A + * typical strategy is to transform the name into a file + * name and then read a "class file" of that name from a file system. + *

+ * Every Class object contains a + * {@link Class#getClassLoader() reference} to the + * ClassLoader that defined it. + *

+ * Class objects for array classes are not created by class loaders, but + * are created automatically as required by the Java runtime. The class + * loader for an array class, as returned by {@link Class#getClassLoader()} + * is the same as the class loader for its element type; if the element + * type is a primitive type, then the array class has no class loader. + *

+ * Applications implement subclasses of ClassLoader in + * order to extend the manner in which the Java virtual machine + * dynamically loads classes. + *

+ * Class loaders may typically be used by security managers to + * indicate security domains. + *

+ * The ClassLoader class uses a delegation model to + * search for classes and resources. Each instance of + * ClassLoader has an associated parent class loader. + * When called upon to find a class or resource, a + * ClassLoader instance will delegate the search for + * the class or resource to its parent class loader before + * attempting to find the class or resource itself. The virtual + * machine's built-in class loader, called the bootstrap class loader, + * does not itself have a parent but may serve as the parent of a + * ClassLoader instance. + *

+ * Normally, the Java virtual machine loads classes from the local + * file system in a platform-dependent manner. For example, on UNIX + * systems, the virtual machine loads classes from the directory + * defined by the CLASSPATH environment variable. + *

+ * However, some classes may not originate from a file; they may + * originate from other sources, such as the network, or they could + * be constructed by an application. The method + * defineClass converts an array of bytes into an + * instance of class Class. Instances of this newly + * defined class can be created using the newInstance + * method in class Class. + *

+ * The methods and constructors of objects created by a class loader + * may reference other classes. To determine the class(es) referred + * to, the Java virtual machine calls the loadClass + * method of the class loader that originally created the class. + *

+ * For example, an application could create a network class loader + * to download class files from a server. Sample code might look like: + *

+ *   ClassLoader loader = new NetworkClassLoader(host, port);
+ *   Object main = loader.loadClass("Main", true).newInstance();
+ *	  . . .
+ * 
+ *

+ * The network class loader subclass must define the methods + * findClass and loadClassData + * to load a class from the network. Once it + * has downloaded the bytes that make up the class, it should use the + * method defineClass to create a class instance. A + * sample implementation is: + *


+ *     class NetworkClassLoader extends ClassLoader {
+ *         String host;
+ *         int port;
+ *
+ *         public Class findClass(String name) {
+ *             byte[] b = loadClassData(name);
+ *             return defineClass(name, b, 0, b.length);
+ *         }
+ *
+ *         private byte[] loadClassData(String name) {
+ *             // load the class data from the connection
+ *              . . .
+ *         }
+ *     }
+ * 

+ * + * @version 1.162, 03/19/02 + * @see java.lang.Class + * @see java.lang.Class#newInstance() + * @see java.lang.ClassLoader#defineClass(byte[], int, int) + * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean) + * @see java.lang.ClassLoader#resolveClass(java.lang.Class) + * @since JDK1.0 + */ +public abstract class ClassLoader { + + private static native void registerNatives(); + static { + registerNatives(); + } + + /* + * If initialization succeed this is set to true and security checks will + * succeed. Otherwise the object is not initialized and the object is + * useless. + */ + private boolean initialized = false; + + /* + * The parent class loader for delegation. + */ + private ClassLoader parent; + + /* + * Hashtable that maps packages to certs + */ + private Hashtable package2certs = new Hashtable(11); + + /* + * shared among all packages with unsigned classes + */ + java.security.cert.Certificate[] nocerts; + + /* + * The classes loaded by this class loader. The only purpose of this + * table is to keep the classes from being GC'ed until the loader + * is GC'ed. + */ + private Vector classes = new Vector(); + + /* + * The initiating protection domains for all classes + * loaded by this loader. + */ + private Set domains = new HashSet(); + + /* + * Called by the VM to record every loaded class with this loader. + */ + void addClass(Class c) { + classes.addElement(c); + } + + /* + * The packages defined in this class loader. Each package name is + * mapped to its corresponding Package object. + */ + private HashMap packages = new HashMap(); + + /** + * Creates a new class loader using the specified parent class loader + * for delegation. + *

+ * If there is a security manager, its checkCreateClassLoader + * method is called. This may result in a security exception. + * + * @param parent the parent class loader + * + * @throws SecurityException if a security manager exists and its + * checkCreateClassLoader method doesn't allow creation of a + * new class loader. + * @see java.lang.SecurityException + * @see java.lang.SecurityManager#checkCreateClassLoader() + * @since 1.2 + */ + protected ClassLoader(ClassLoader parent) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkCreateClassLoader(); + } + this.parent = parent; + initialized = true; + } + + /** + * Creates a new class loader using the ClassLoader + * returned by the method getSystemClassLoader() as the + * parent class loader. + *

+ * If there is a security manager, its checkCreateClassLoader + * method is called. This may result in a security exception. + * + * @throws SecurityException + * if a security manager exists and its checkCreateClassLoader + * method doesn't allow creation of a new class loader. + * + * @see java.lang.SecurityException + * @see java.lang.SecurityManager#checkCreateClassLoader() + */ + protected ClassLoader() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkCreateClassLoader(); + } + this.parent = getSystemClassLoader(); + initialized = true; + } + + /** + * Loads the class with the specified name. This method searches for + * classes in the same manner as the {@link #loadClass(String, boolean)} + * method. It is called by the Java virtual machine to resolve class + * references. Calling this method is equivalent to calling + * loadClass(name, false). + * + * @param name the name of the class + * @return the resulting Class object + * @exception ClassNotFoundException if the class was not found + */ + public Class loadClass(String name) throws ClassNotFoundException + { + return loadClass(name, false); + } + + /** + * Loads the class with the specified name. The default implementation of + * this method searches for classes in the following order:

+ * + *

    + *
  1. Call {@link #findLoadedClass(String)} to check if the class has + * already been loaded.

    + *

  2. Call the loadClass method on the parent class + * loader. If the parent is null the class loader + * built-in to the virtual machine is used, instead.

    + *

  3. Call the {@link #findClass(String)} method to find the class.

    + *

+ * + * If the class was found using the above steps, and the + * resolve flag is true, this method will then call the + * {@link #resolveClass(Class)} method on the resulting class object. + *

+ * From the Java 2 SDK, v1.2, subclasses of ClassLoader are + * encouraged to override + * {@link #findClass(String)}, rather than this method.

+ * + * @param name the name of the class + * @param resolve if true then resolve the class + * @return the resulting Class object + * @exception ClassNotFoundException if the class could not be found + */ + protected synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + // First, check if the class has already been loaded + Class c = findLoadedClass(name); + if (c == null) { + try { + if (parent != null) { + c = parent.loadClass(name, false); + } else { + c = findBootstrapClass0(name); + } + } catch (ClassNotFoundException e) { + // If still not found, then call findClass in order + // to find the class. + c = findClass(name); + } + } + if (resolve) { + resolveClass(c); + } + return c; + } + + /* + * This method is called by the virtual machine to load + * a class. + */ + private synchronized Class loadClassInternal(String name) + throws ClassNotFoundException { + + return loadClass(name); + } + + private void checkPackageAccess(Class cls, ProtectionDomain pd) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + final String name = cls.getName(); + final int i = name.lastIndexOf('.'); + if (i != -1) { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + sm.checkPackageAccess(name.substring(0, i)); + return null; + } + }, new AccessControlContext(new ProtectionDomain[] {pd})); + } + } + domains.add(pd); + } + + /** + * Finds the specified class. This method should be overridden + * by class loader implementations that follow the new delegation model + * for loading classes, and will be called by the loadClass + * method after checking the parent class loader for the requested class. + * The default implementation throws ClassNotFoundException. + * + * @param name the name of the class + * @return the resulting Class object + * @exception ClassNotFoundException if the class could not be found + * @since 1.2 + */ + protected Class findClass(String name) throws ClassNotFoundException { + throw new ClassNotFoundException(name); + } + + /** + * Converts an array of bytes into an instance of class + * Class. Before the Class can be used it must be + * resolved. This method is deprecated in favor of the version + * that takes the class name as its first argument, and is more + * secure. + * + * @param b the bytes that make up the class data. The bytes in + * positions off through off+len-1 + * should have the format of a valid class file as defined + * by the + * Java + * Virtual Machine Specification. + * @param off the start offset in b of the class data + * @param len the length of the class data + * @return the Class object that was created from the + * specified class data + * @exception ClassFormatError if the data did not contain a valid class + * @exception IndexOutOfBoundsException if either off or + * len is negative, or if + * off+len is greater than b.length. + * @see ClassLoader#loadClass(java.lang.String, boolean) + * @see ClassLoader#resolveClass(java.lang.Class) + * @deprecated Replaced by defineClass(java.lang.String, byte[], int, int) + */ + protected final Class defineClass(byte[] b, int off, int len) + throws ClassFormatError + { + return defineClass(null, b, off, len, null); + } + + /** + * Converts an array of bytes into an instance of class Class. + * Before the Class can be used it must be resolved. + *

+ * This method assigns a default ProtectionDomain to + * the newly defined class. The ProtectionDomain + * contains the set of permissions granted when + * a call to Policy.getPolicy().getPermissions() is made with + * a code source of null,null. The default domain is + * created on the first invocation of defineClass, and + * re-used on subsequent calls. + *

+ * To assign a specific ProtectionDomain to the class, + * use the defineClass method that takes a + * ProtectionDomain as one of its arguments. + * + * @param name the expected name of the class, or null + * if not known, using '.' and not '/' as the separator + * and without a trailing ".class" suffix. + * @param b the bytes that make up the class data. The bytes in + * positions off through off+len-1 + * should have the format of a valid class file as defined + * by the + * Java + * Virtual Machine Specification. + * @param off the start offset in b of the class data + * @param len the length of the class data + * @return the Class object that was created from the + * specified class data + * @exception ClassFormatError if the data did not contain a valid class + * @exception IndexOutOfBoundsException if either off or + * len is negative, or if + * off+len is greater than b.length. + * @exception SecurityException if an attempt is made to add this class + * to a package that contains classes that were signed by + * a different set of certificates than this class (which + * is unsigned), or if the class name begins with "java.". + * + * @see ClassLoader#loadClass(java.lang.String, boolean) + * @see ClassLoader#resolveClass(java.lang.Class) + * @see java.security.ProtectionDomain + * @see java.security.Policy + * @see java.security.CodeSource + * @see java.security.SecureClassLoader + * @since JDK1.1 + */ + protected final Class defineClass(String name, byte[] b, int off, int len) + throws ClassFormatError + { + return defineClass(name, b, off, len, null); + } + + /** + * Converts an array of bytes into an instance of class Class, + * with an optional ProtectionDomain. If the domain is null, + * then a default domain will be assigned to the class as specified + * in the documentation for {@link #defineClass(String,byte[],int,int)}. + * Before the class can be used it must be resolved. + * + *

The first class defined in a package determines the exact set of + * certificates that all subsequent classes defined in that package must + * contain. The set of certificates for a class is obtained from the + * CodeSource within the ProtectionDomain of + * the class. Any classes added to that package must contain + * the same set of certificates or a SecurityException + * will be thrown. Note that if the name argument is + * null, this check is not performed. You should always pass in the + * name of the class you are defining as well as the bytes. This + * ensures that the class you are defining is indeed the class + * you think it is. + * + *

The specified class name cannot begin with "java.", since all + * classes in the java.* packages can only be defined by the bootstrap + * class loader. If the name parameter is not null, it + * must be equal to the name of the class specified by the byte + * array b, otherwise a ClassFormatError is raised. + * + * @param name the expected name of the class, or null + * if not known, using '.' and not '/' as the separator + * and without a trailing ".class" suffix. + * @param b the bytes that make up the class data. The bytes in + * positions off through off+len-1 + * should have the format of a valid class file as defined + * by the + * Java + * Virtual Machine Specification. + * @param off the start offset in b of the class data + * @param len the length of the class data + * @param protectionDomain the ProtectionDomain of the class + * @return the Class object created from the data, + * and optional ProtectionDomain. + * @exception ClassFormatError if the data did not contain a valid class + * @exception IndexOutOfBoundsException if either off or + * len is negative, or if + * off+len is greater than b.length. + * @exception SecurityException if an attempt is made to add this class + * to a package that contains classes that were signed by + * a different set of certificates than this class, or if + * the class name begins with "java.". + */ + protected final Class defineClass(String name, byte[] b, int off, int len, + ProtectionDomain protectionDomain) + throws ClassFormatError + { + check(); + if ((name != null) && name.startsWith("java.")) { + throw new SecurityException("Prohibited package name: " + + name.substring(0, name.lastIndexOf('.'))); + } + if (protectionDomain == null) { + protectionDomain = getDefaultDomain(); + } + + if (name != null) + checkCerts(name, protectionDomain.getCodeSource()); + + Class c = null; + + try + { + c = defineClass0(name, b, off, len, protectionDomain); + } + catch (ClassFormatError cfe) + { + // Class format error - try to transform the bytecode and + // define the class again + // + Object[] transformers = ClassFileTransformer.getTransformers(); + + for (int i=0; transformers != null && i < transformers.length; i++) + { + try + { + // Transform byte code using transformer + byte[] tb = ((ClassFileTransformer) transformers[i]).transform(b, off, len); + c = defineClass0(name, tb, 0, tb.length, protectionDomain); + break; + } + catch (ClassFormatError cfe2) + { + // If ClassFormatError occurs, try next transformer + } + } + + // Rethrow original ClassFormatError if unable to transform + // bytecode to well-formed + // + if (c == null) + throw cfe; + } + + if (protectionDomain.getCodeSource() != null) { + java.security.cert.Certificate certs[] = + protectionDomain.getCodeSource().getCertificates(); + if (certs != null) + setSigners(c, certs); + } + return c; + } + + private synchronized void checkCerts(String name, CodeSource cs) + { + int i = name.lastIndexOf('.'); + String pname = (i == -1) ? "" : name.substring(0,i); + java.security.cert.Certificate[] pcerts = + (java.security.cert.Certificate[]) package2certs.get(pname); + if (pcerts == null) { + // first class in this package gets to define which + // certificates must be the same for all other classes + // in this package + if (cs != null) { + pcerts = cs.getCertificates(); + } + if (pcerts == null) { + if (nocerts == null) + nocerts = new java.security.cert.Certificate[0]; + pcerts = nocerts; + } + package2certs.put(pname, pcerts); + } else { + java.security.cert.Certificate[] certs = null; + if (cs != null) { + certs = cs.getCertificates(); + } + + if (!compareCerts(pcerts,certs)) { + throw new SecurityException("class \""+ name+ + "\"'s signer information does not match signer information of other classes in the same package"); + } + } + } + + /** + * check to make sure the certs for the new class (certs) are + * the same as the certs for the first class inserted + * in the package (pcerts) + */ + private boolean compareCerts(java.security.cert.Certificate[] pcerts, + java.security.cert.Certificate[] certs) + { + // certs can be null, indicating no certs. + if ((certs == null) || (certs.length == 0)) { + return pcerts.length == 0; + } + + // the length must be the same at this point + if (certs.length != pcerts.length) + return false; + + // go through and make sure all the certs in one array + // are in the other and vice-versa. + boolean match; + for (int i=0; i < certs.length; i++) { + match = false; + for (int j=0; j < pcerts.length; j++) { + if (certs[i].equals(pcerts[j])) { + match = true; + break; + } + } + if (!match) return false; + } + + // now do the same for pcerts + for (int i=0; i < pcerts.length; i++) { + match = false; + for (int j=0; j < certs.length; j++) { + if (pcerts[i].equals(certs[j])) { + match = true; + break; + } + } + if (!match) return false; + } + + return true; + } + + /** + * Links the specified class. + * This (misleadingly named) method may be used by a class loader to + * link a class. If the class c has already been linked, + * then this method simply returns. Otherwise, the class is linked + * as described in the "Execution" chapter of the Java Language + * Specification. + * + * @param c the class to link + * @exception NullPointerException if c is null. + * @see java.lang.ClassLoader#defineClass(java.lang.String,byte[],int,int) + */ + protected final void resolveClass(Class c) { + check(); + resolveClass0(c); + } + + /** + * Finds a class with the specified name, loading it if necessary.

+ * + * Prior to the Java 2 SDK, this method loads a class from the local file + * system in a platform-dependent manner, and returns a class object + * that has no associated class loader.

+ * + * Since the Java 2 SDK v1.2, this method loads the class through the + * system class loader(see {@link #getSystemClassLoader()}). Class objects + * returned might have ClassLoaders associated with them. + * Subclasses of ClassLoader need not usually call this + * method, because most class loaders need to override just {@link + * #findClass(String)}.

+ * + * @param name the name of the class that is to be found + * @return the Class object for the specified + * name + * @exception ClassNotFoundException if the class could not be found + * @see #ClassLoader(ClassLoader) + * @see #getParent() + */ + protected final Class findSystemClass(String name) + throws ClassNotFoundException + { + check(); + ClassLoader system = getSystemClassLoader(); + if (system == null) { + return findBootstrapClass(name); + } + return system.loadClass(name); + } + + /** + * Returns the parent class loader for delegation. Some implementations + * may use null to represent the bootstrap class + * loader. This method will return null in such + * implementations if this class loader's parent is the bootstrap + * class loader. + *

+ * If a security manager is present, and the caller's class loader is + * not null and is not an ancestor of this class loader, then + * this method calls the security manager's checkPermission + * method with a RuntimePermission("getClassLoader") + * permission to ensure it's ok to access the parent class loader. + * If not, a SecurityException will be thrown. + * + * @return the parent ClassLoader + * @throws SecurityException + * if a security manager exists and its + * checkPermission method doesn't allow + * access to this class loader's parent class loader. + * + * @see SecurityManager#checkPermission + * @see java.lang.RuntimePermission + * + * @since 1.2 + */ + public final ClassLoader getParent() { + if (parent == null) + return null; + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + ClassLoader ccl = getCallerClassLoader(); + if (ccl != null && !isAncestor(ccl)) { + sm.checkPermission(getGetClassLoaderPerm()); + } + } + return parent; + } + + /** + * Sets the signers of a class. This should be called after defining a + * class. + * + * @param c the Class object + * @param signers the signers for the class + * @since JDK1.1 + */ + protected final void setSigners(Class c, Object[] signers) { + check(); + c.setSigners(signers); + } + + private Class findBootstrapClass0(String name) + throws ClassNotFoundException { + check(); + return findBootstrapClass(name); + } + + private native Class defineClass0(String name, byte[] b, int off, int len, + ProtectionDomain pd); + private native void resolveClass0(Class c); + private native Class findBootstrapClass(String name) + throws ClassNotFoundException; + + /* + * Check to make sure the class loader has been initialized. + */ + private void check() { + if (!initialized) { + throw new SecurityException("ClassLoader object not initialized"); + } + } + + /** + * Finds the class with the given name if it had been previously loaded + * through this class loader. + * + * @param name the class name + * @return the Class object, or null if + * the class has not been loaded + * @since JDK1.1 + */ + protected native final Class findLoadedClass(String name); + + /** + * Finds the resource with the given name. A resource is some data + * (images, audio, text, etc) that can be accessed by class code in a way + * that is independent of the location of the code.

+ * + * The name of a resource is a "/"-separated path name that identifies + * the resource.

+ * + * This method will first search the parent class loader for the resource; + * if the parent is null the path of the class loader + * built-in to the virtual machine is searched. That failing, this method + * will call findResource to find the resource.

+ * + * @param name resource name + * @return a URL for reading the resource, or null if + * the resource could not be found or the caller doesn't have + * adequate privileges to get the resource. + * @since JDK1.1 + * @see #findResource(String) + */ + public URL getResource(String name) { + URL url; + if (parent != null) { + url = parent.getResource(name); + } else { + url = getBootstrapResource(name); + } + if (url == null) { + url = findResource(name); + } + return url; + } + + /** + * Finds all the resources with the given name. A resource is some data + * (images, audio, text, etc) that can be accessed by class code in a way + * that is independent of the location of the code.

+ * + * The name of a resource is a "/"-separated path name that identifies the + * resource.

+ * + * The search order is described in the documentation for {@link + * #getResource(String)}.

+ * + * @param name resource name + * @return an enumeration of URL to the resource. If no resources could + * be found, the enumeration will be empty. Resources that the + * doesn't have access to will not be in the enumeration. + * @throws IOException if I/O errors occur + * @since 1.2 + * @see #getResource + * @see #findResources + */ + public final Enumeration getResources(String name) throws IOException { + Enumeration[] tmp = new Enumeration[2]; + if (parent != null) { + tmp[0] = parent.getResources(name); + } else { + tmp[0] = getBootstrapResources(name); + } + tmp[1] = findResources(name); + + return new CompoundEnumeration(tmp); + } + + /** + * Returns an Enumeration of URLs representing all the resources with + * the given name. Class loader implementations should override this + * method to specify where to load resources from. + * + * @param name the resource name + * @return an Enumeration of URLs for the resources + * @throws IOException if I/O errors occur + * @since 1.2 + */ + protected Enumeration findResources(String name) throws IOException { + return new CompoundEnumeration(new Enumeration[0]); + } + + /** + * Finds the resource with the given name. Class loader + * implementations should override this method to specify where to + * find resources. + * + * @param name the resource name + * @return a URL for reading the resource, or null + * if the resource could not be found + * @since 1.2 + */ + protected URL findResource(String name) { + return null; + } + + /** + * Find a resource of the specified name from the search path used to load + * classes.

+ * + * In JDK1.1, the search path used is that of the virtual machine's + * built-in class loader.

+ * + * Since the Java 2 SDK v1.2, this method locates the resource through the system class + * loader (see {@link #getSystemClassLoader()}). + * + * @param name the resource name + * @return a URL for reading the resource, or null if + * the resource could not be found + * @since JDK1.1 + */ + public static URL getSystemResource(String name) { + ClassLoader system = getSystemClassLoader(); + if (system == null) { + return getBootstrapResource(name); + } + return system.getResource(name); + } + + /** + * Find resources from the VM's built-in classloader. + */ + private static URL getBootstrapResource(String name) { + URLClassPath ucp = getBootstrapClassPath(); + Resource res = ucp.getResource(name); + return res != null ? res.getURL() : null; + } + + /** + * Finds all resources of the specified name from the search path used to + * load classes. The resources thus found are returned as an + * Enumeration of URL objects.

+ * + * The search order is described in the documentation for {@link + * #getSystemResource(String)}.

+ * + * @param name the resource name + * @return an enumeration of resource URLs + * @throws IOException if I/O errors occur + * @since 1.2 + */ + public static Enumeration getSystemResources(String name) + throws IOException + { + ClassLoader system = getSystemClassLoader(); + if (system == null) { + return getBootstrapResources(name); + } + return system.getResources(name); + } + + /** + * Find resources from the VM's built-in classloader. + */ + private static Enumeration getBootstrapResources(String name) + throws IOException + { + final Enumeration e = getBootstrapClassPath().getResources(name); + return new Enumeration () { + public Object nextElement() { + return ((Resource)e.nextElement()).getURL(); + } + public boolean hasMoreElements() { + return e.hasMoreElements(); + } + }; + } + + /* + * Returns the URLClassPath that is used for finding system resources. + */ + static URLClassPath getBootstrapClassPath() { + if (bootstrapClassPath == null) { + bootstrapClassPath = sun.misc.Launcher.getBootstrapClassPath(); + } + return bootstrapClassPath; + } + + private static URLClassPath bootstrapClassPath; + + /** + * Returns an input stream for reading the specified resource. + * + * The search order is described in the documentation for {@link + * #getResource(String)}.

+ * + * @param name the resource name + * @return an input stream for reading the resource, or null + * if the resource could not be found + * @since JDK1.1 + */ + public InputStream getResourceAsStream(String name) { + URL url = getResource(name); + try { + return url != null ? url.openStream() : null; + } catch (IOException e) { + return null; + } + } + + /** + * Open for reading, a resource of the specified name from the search path + * used to load classes.

+ * + * The search order is described in the documentation for {@link + * #getSystemResource(String)}.

+ * + * @param name the resource name + * @return an input stream for reading the resource, or null + * if the resource could not be found + * @since JDK1.1 + */ + public static InputStream getSystemResourceAsStream(String name) { + URL url = getSystemResource(name); + try { + return url != null ? url.openStream() : null; + } catch (IOException e) { + return null; + } + } + + /** + * Returns the system class loader for delegation. This is the default + * delegation parent for new ClassLoader instances, and + * is typically the class loader used to start the application. + *

+ * This method is first invoked early in the runtime's startup + * sequence, at which point it creates the system class loader + * and sets it as the context class loader of the invoking + * Thread. + *

+ * The default system class loader is an implementation-dependent + * instance of this class. + *

+ * If the system property java.system.class.loader is + * defined when this method is first invoked then the value of that + * property is taken to be the name of a class that will be returned as + * the system class loader. The class is loaded using the default system + * class loader and must define a public constructor that takes a single + * parameter of type ClassLoader which is used + * as the delegation parent. An instance is then created using this + * constructor with the default system class loader as the parameter. + * The resulting class loader is defined to be the system class loader. + *

+ * If a security manager is present, and the caller's class loader is + * not null and the caller's class loader is not the same as or an ancestor of + * the system class loader, then + * this method calls the security manager's checkPermission + * method with a RuntimePermission("getClassLoader") + * permission to ensure it's ok to access the system class loader. + * If not, a SecurityException will be thrown. + * + * @return the system ClassLoader for delegation, or + * null if none + * @throws SecurityException + * if a security manager exists and its + * checkPermission method doesn't allow + * access to the system class loader. + * @throws IllegalStateException + * if invoked recursively during the construction + * of the class loader specified by the + * java.system.class.loader property. + * @throws Error + * if the system property java.system.class.loader + * is defined but the named class could not be loaded, the + * provider class does not define the required constructor, or an + * exception is thrown by that constructor when it is invoked. The + * underlying cause of the error can be retrieved via the + * {@link Throwable#getCause()} method. + * @see SecurityManager#checkPermission + * @see java.lang.RuntimePermission + * @revised 1.4 + */ + public static ClassLoader getSystemClassLoader() { + initSystemClassLoader(); + if (scl == null) { + return null; + } + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + ClassLoader ccl = getCallerClassLoader(); + if (ccl != null && ccl != scl && !scl.isAncestor(ccl)) { + sm.checkPermission(getGetClassLoaderPerm()); + } + } + return scl; + } + + private static synchronized void initSystemClassLoader() { + if (!sclSet) { + if (scl != null) + throw new IllegalStateException("recursive call"); + sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); + if (l != null) { + Throwable oops = null; + scl = l.getClassLoader(); + try { + PrivilegedExceptionAction a; + a = new SystemClassLoaderAction(scl); + scl = (ClassLoader) AccessController.doPrivileged(a); + } catch (PrivilegedActionException pae) { + oops = pae.getCause(); + if (oops instanceof InvocationTargetException) { + oops = oops.getCause(); + } + } + if (oops != null) { + if (oops instanceof Error) { + throw (Error) oops; + } else { + // wrap the exception + throw new Error(oops); + } + } + } + sclSet = true; + } + } + + // Returns true if the specified class loader can be found + // in this class loader's delegation chain. + boolean isAncestor(ClassLoader cl) { + ClassLoader acl = this; + do { + acl = acl.parent; + if (cl == acl) { + return true; + } + } while (acl != null); + return false; + } + + // Returns the caller's class loader, or null if none. + // NOTE this must always be called when there is exactly one + // intervening frame from the core libraries on the stack between + // this method's invocation and the desired caller. + static ClassLoader getCallerClassLoader() { + // NOTE use of more generic Reflection.getCallerClass() + Class caller = Reflection.getCallerClass(3); + // This can be null if the VM is requesting it + if (caller == null) { + return null; + } + // Circumvent security check since this is package-private + return caller.getClassLoader0(); + } + + // The class loader for the system + private static ClassLoader scl; + + // Set to true once the system class loader has been set + private static boolean sclSet; + + // Permission to access system or parent class loader + private static RuntimePermission getClassLoaderPerm = null; + + static RuntimePermission getGetClassLoaderPerm() + { + if (getClassLoaderPerm == null) + getClassLoaderPerm = new RuntimePermission("getClassLoader"); + return getClassLoaderPerm; + } + + /** + * Defines a package by name in this ClassLoader. This allows class + * loaders to define the packages for their classes. Packages must be + * created before the class is defined, and package names must be + * unique within a class loader and cannot be redefined or changed + * once created. + * + * @param name the package name + * @param specTitle the specification title + * @param specVersion the specification version + * @param specVendor the specification vendor + * @param implTitle the implementation title + * @param implVersion the implementation version + * @param implVendor the implementation vendor + * @param sealBase If not null, then this package is sealed with + * respect to the given code source URL. Otherwise, + * the package is not sealed. + * @return the newly defined Package object + * @exception IllegalArgumentException if package name duplicates an + * existing package either in this class loader or one of + * its ancestors + * @since 1.2 + */ + protected Package definePackage(String name, String specTitle, + String specVersion, String specVendor, + String implTitle, String implVersion, + String implVendor, URL sealBase) + throws IllegalArgumentException + { + synchronized (packages) { + Package pkg = getPackage(name); + if (pkg != null) { + throw new IllegalArgumentException(name); + } + pkg = new Package(name, specTitle, specVersion, specVendor, + implTitle, implVersion, implVendor, + sealBase); + packages.put(name, pkg); + return pkg; + } + } + + /** + * Returns a Package that has been defined by this class loader or any + * of its ancestors. + * + * @param name the package name + * @return the Package corresponding to the given name, or null if not + * found + * @since 1.2 + */ + protected Package getPackage(String name) { + synchronized (packages) { + Package pkg = (Package)packages.get(name); + if (pkg == null) { + if (parent != null) { + pkg = parent.getPackage(name); + } else { + pkg = Package.getSystemPackage(name); + } + if (pkg != null) { + packages.put(name, pkg); + } + } + return pkg; + } + } + + /** + * Returns all of the Packages defined by this class loader and its + * ancestors. + * + * @return the array of Package objects defined by this + * ClassLoader + * @since 1.2 + */ + protected Package[] getPackages() { + Map map; + synchronized (packages) { + map = (Map)packages.clone(); + } + Package[] pkgs; + if (parent != null) { + pkgs = parent.getPackages(); + } else { + pkgs = Package.getSystemPackages(); + } + if (pkgs != null) { + for (int i = 0; i < pkgs.length; i++) { + String pkgName = pkgs[i].getName(); + if (map.get(pkgName) == null) { + map.put(pkgName, pkgs[i]); + } + } + } + return (Package[])map.values().toArray(new Package[map.size()]); + } + + /** + * Returns the absolute path name of a native library. The VM + * invokes this method to locate the native libraries that belong + * to classes loaded with this class loader. If this method returns + * null, the VM searches the library along the path + * specified as the java.library.path property. + * + * @param libname the library name + * @return the absolute path of the native library + * @see java.lang.System#loadLibrary(java.lang.String) + * @see java.lang.System#mapLibraryName(java.lang.String) + * @since 1.2 + */ + protected String findLibrary(String libname) { + return null; + } + + /** + * The inner class NativeLibrary denotes a loaded native library + * instance. Every classloader contains a vector of loaded native + * libraries in the private field nativeLibraries. + * The native libraries loaded into the system are entered into + * the systemNativeLibraries vector. + * + * Every native library reuqires a particular version of JNI. This + * is denoted by the private jniVersion field. This field is set + * by the VM when it loads the library, and used by the VM to pass + * the correct version of JNI to the native methods. + * + * @version 1.162, 03/19/02 + * @see java.lang.ClassLoader + * @since 1.2 + */ + static class NativeLibrary { + /* opaque handle to native library, used in native code. */ + long handle; + /* the version of JNI environment the native library requires. */ + private int jniVersion; + /* the class from which the library is loaded, also indicates + the loader this native library belongs. */ + private Class fromClass; + /* the canonicalized name of the native library. */ + String name; + + native void load(String name); + native long find(String name); + native void unload(); + + public NativeLibrary(Class fromClass, String name) { + this.name = name; + this.fromClass = fromClass; + } + + protected void finalize() { + synchronized (loadedLibraryNames) { + if (fromClass.getClassLoader() != null && handle != 0) { + /* remove the native library name */ + int size = loadedLibraryNames.size(); + for (int i = 0; i < size; i++) { + if (name.equals(loadedLibraryNames.elementAt(i))) { + loadedLibraryNames.removeElementAt(i); + break; + } + } + /* unload the library. */ + ClassLoader.nativeLibraryContext.push(this); + try { + unload(); + } finally { + ClassLoader.nativeLibraryContext.pop(); + } + } + } + } + /* Called in the VM to determine the context class in + JNI_Load/JNI_Unload */ + static Class getFromClass() { + return ((NativeLibrary) + (ClassLoader.nativeLibraryContext.peek())).fromClass; + } + } + + /* the "default" domain. Set as the default ProtectionDomain + * on newly created classses. + */ + private ProtectionDomain defaultDomain = null; + + /* + * returns (and initializes) the default domain. + */ + + private synchronized ProtectionDomain getDefaultDomain() { + if (defaultDomain == null) { + CodeSource cs = new CodeSource(null, null); + defaultDomain = new ProtectionDomain(cs, null, this, null); + } + return defaultDomain; + } + + /* All native library names we've loaded. */ + private static Vector loadedLibraryNames = new Vector(); + /* Native libraries belonging to system classes. */ + private static Vector systemNativeLibraries = new Vector(); + /* Native libraries associated with the class loader. */ + private Vector nativeLibraries = new Vector(); + + /* native libraries being loaded/unloaded. */ + private static Stack nativeLibraryContext = new Stack(); + + /* The paths searched for libraries */ + static private String usr_paths[]; + static private String sys_paths[]; + + private static String[] initializePath(String propname) { + String ldpath = System.getProperty(propname, ""); + String ps = File.pathSeparator; + int ldlen = ldpath.length(); + int i, j, n; + // Count the separators in the path + i = ldpath.indexOf(ps); + n = 0; + while (i >= 0) { + n++; + i = ldpath.indexOf(ps, i+1); + } + + // allocate the array of paths - n :'s = n + 1 path elements + String[] paths = new String[n + 1]; + + // Fill the array with paths from the ldpath + n = i = 0; + j = ldpath.indexOf(ps); + while (j >= 0) { + if (j - i > 0) { + paths[n++] = ldpath.substring(i, j); + } else if (j - i == 0) { + paths[n++] = "."; + } + i = j + 1; + j = ldpath.indexOf(ps, i); + } + paths[n] = ldpath.substring(i, ldlen); + return paths; + } + + + /* Called in the java.lang.Runtime class to implement load + * and loadLibrary. + */ + static void loadLibrary(Class fromClass, String name, + boolean isAbsolute) { + ClassLoader loader = + (fromClass == null) ? null : fromClass.getClassLoader(); + if (sys_paths == null) { + usr_paths = initializePath("java.library.path"); + sys_paths = initializePath("sun.boot.library.path"); + } + if (isAbsolute) { + if (loadLibrary0(fromClass, new File(name))) { + return; + } + throw new UnsatisfiedLinkError("Can't load library: " + name); + } + if (loader != null) { + String libfilename = loader.findLibrary(name); + if (libfilename != null) { + File libfile = new File(libfilename); + if (!libfile.isAbsolute()) { + throw new UnsatisfiedLinkError( + "ClassLoader.findLibrary failed to return an absolute path: " + libfilename); + } + if (loadLibrary0(fromClass, libfile)) { + return; + } + throw new UnsatisfiedLinkError ("Can't load " + libfilename); + } + } + for (int i = 0 ; i < sys_paths.length ; i++) { + File libfile = new File(sys_paths[i], System.mapLibraryName(name)); + if (loadLibrary0(fromClass, libfile)) { + return; + } + } + if (loader != null) { + for (int i = 0 ; i < usr_paths.length ; i++) { + File libfile = new File(usr_paths[i], + System.mapLibraryName(name)); + if (loadLibrary0(fromClass, libfile)) { + return; + } + } + } + // Oops, it failed + throw new UnsatisfiedLinkError("no " + name + " in java.library.path"); + } + + private static boolean loadLibrary0(Class fromClass, final File file) { + Boolean exists = (Boolean) + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return new Boolean(file.exists()); + } + }); + if (!exists.booleanValue()) { + return false; + } + String name; + try { + name = file.getCanonicalPath(); + } catch (IOException e) { + return false; + } + ClassLoader loader = + (fromClass == null) ? null : fromClass.getClassLoader(); + Vector libs = + loader != null ? loader.nativeLibraries : systemNativeLibraries; + synchronized (libs) { + int size = libs.size(); + for (int i = 0; i < size; i++) { + NativeLibrary lib = (NativeLibrary)libs.elementAt(i); + if (name.equals(lib.name)) { + return true; + } + } + + synchronized (loadedLibraryNames) { + if (loadedLibraryNames.contains(name)) { + throw new UnsatisfiedLinkError + ("Native Library " + + name + + " already loaded in another classloader"); + } + /* If the library is being loaded (must be by + * the same thread, because Runtime.load and + * Runtime.loadLibrary are synchronous). The + * reason is can occur is that the JNI_OnLoad + * function can cause another loadLibrary call. + * + * Thus we can use a static stack to hold the list + * of libraries we are loading. + * + * If there is a pending load operation for the + * library, we immediately return success; otherwise, + * we raise UnsatisfiedLinkError. + */ + int n = nativeLibraryContext.size(); + for (int i = 0; i < n; i++) { + NativeLibrary lib = (NativeLibrary) + nativeLibraryContext.elementAt(i); + if (name.equals(lib.name)) { + if (loader == lib.fromClass.getClassLoader()) { + return true; + } else { + throw new UnsatisfiedLinkError + ("Native Library " + + name + + " is being loaded in another classloader"); + } + } + } + NativeLibrary lib = new NativeLibrary(fromClass, name); + nativeLibraryContext.push(lib); + try { + lib.load(name); + } finally { + nativeLibraryContext.pop(); + } + if (lib.handle != 0) { + loadedLibraryNames.addElement(name); + libs.addElement(lib); + return true; + } + return false; + } + } + } + + /* Called in the VM class linking code. */ + static long findNative(ClassLoader loader, String name) { + Vector libs = + loader != null ? loader.nativeLibraries : systemNativeLibraries; + synchronized (libs) { + int size = libs.size(); + for (int i = 0; i < size; i++) { + NativeLibrary lib = (NativeLibrary)libs.elementAt(i); + long entry = lib.find(name); + if (entry != 0) + return entry; + } + } + return 0; + } + + /* + * The default toggle for assertion checking. + */ + private boolean defaultAssertionStatus = false; + + /* + * Maps String packageName to Boolean package default assertion status + * Note that the default package is placed under a null map key. + * If this field is null then we are delegating assertion status queries + * to the VM, i.e., none of this ClassLoader's assertion status + * modification methods have been called. + */ + private Map packageAssertionStatus = null; + + /* + * Maps String fullyQualifiedClassName to Boolean assertionStatus + * If this field is null then we are delegating assertion status queries + * to the VM, i.e., none of this ClassLoader's assertion status + * modification methods have been called. + */ + Map classAssertionStatus = null; + + /** + * Sets the default assertion status for this class loader. This setting + * determines whether classes loaded by this class loader and initialized + * in the future will have assertions enabled or disabled by default. + * This setting may be overridden on a per-package or per-class basis by + * invoking {@link #setPackageAssertionStatus(String,boolean)} or {@link + * #setClassAssertionStatus(String,boolean)}. + * + * @param enabled true if classes loaded by this class loader + * will henceforth have assertions enabled by default, + * false if they will have assertions disabled by default. + * @since 1.4 + */ + public synchronized void setDefaultAssertionStatus(boolean enabled) { + if (classAssertionStatus == null) + initializeJavaAssertionMaps(); + + defaultAssertionStatus = enabled; + } + + /** + * Sets the package default assertion status for the named + * package. The package default assertion status determines the + * assertion status for classes initialized in the future that belong + * to the named package or any of its "subpackages." + *

+ * A subpackage of a package named p is any package whose name + * begins with "p." . For example, javax.swing.text is + * a subpackage of javax.swing, and both java.util + * and java.lang.reflect are subpackages of java. + *

+ * In the event that multiple package defaults apply to a given + * class, the package default pertaining to the most specific package + * takes precedence over the others. For example, if + * javax.lang and javax.lang.reflect both have + * package defaults associated with them, the latter package + * default applies to classes in javax.lang.reflect. + *

+ * Package defaults take precedence over the class loader's default + * assertion status, and may be overridden on a per-class basis by + * invoking {@link #setClassAssertionStatus(String,boolean)}. + * + * @param packageName the name of the package whose package default + * assertion status is to be set. A null value + * indicates the unnamed package that is "current" + * (JLS 7.4.2). + * @param enabled true if classes loaded by this classloader + * and belonging to the named package or any of its subpackages + * will have assertions enabled by default, false if they + * will have assertions disabled by default. + * @since 1.4 + */ + public synchronized void setPackageAssertionStatus(String packageName, + boolean enabled) + { + if (packageAssertionStatus == null) + initializeJavaAssertionMaps(); + + packageAssertionStatus.put(packageName, Boolean.valueOf(enabled)); + } + + /** + * Sets the desired assertion status for the named top-level class in + * this class loader and any nested classes contained therein. + * This setting takes precedence over the class loader's default + * assertion status, and over any applicable per-package default. + * This method has no effect if the named class has already been + * initialized. (Once a class is initialized, its assertion status cannot + * change.) + *

+ * If the named class is not a top-level class, this call will have no + * effect on the actual assertion status of any class, and its return + * value is undefined. + * + * @param className the fully qualified class name of the top-level class + * whose assertion status is to be set. + * @param enabled true if the named class is to have assertions + * enabled when (and if) it is initialized, false if the + * class is to have assertions disabled. + * @since 1.4 + */ + public synchronized void setClassAssertionStatus(String className, + boolean enabled) + { + if (classAssertionStatus == null) + initializeJavaAssertionMaps(); + + classAssertionStatus.put(className, Boolean.valueOf(enabled)); + } + + /** + * Sets the default assertion status for this class loader to + * false and discards any package defaults or class assertion + * status settings associated with the class loader. This call is + * provided so that class loaders can be made to ignore any command line + * or persistent assertion status settings and "start with a clean slate." + * + * @since 1.4 + */ + public synchronized void clearAssertionStatus() { + /* + * Whether or not "Java assertion maps" are initialized, set + * them to empty maps, effectively ignoring any present settings. + */ + classAssertionStatus = new HashMap(); + packageAssertionStatus = new HashMap(); + + defaultAssertionStatus = false; + } + + /** + * Returns the assertion status that would be assigned to the specified + * class if it were to be initialized at the time this method is invoked. + * If the named class has had its assertion status set, the most recent + * setting will be returned; otherwise, if any package default assertion + * status pertains to this class, the most recent setting for the most + * specific pertinent package default assertion status is returned; + * otherwise, this class loader's default assertion status is returned. + * + * @param className the fully qualified class name of the class whose + * desired assertion status is being queried. + * @return the desired assertion status of the specified class. + * @see #setClassAssertionStatus(String,boolean) + * @see #setPackageAssertionStatus(String,boolean) + * @see #setDefaultAssertionStatus(boolean) + * @since 1.4 + */ + synchronized boolean desiredAssertionStatus(String className) { + Boolean result; + + // assert classAssertionStatus != null; + // assert packageAssertionStatus != null; + + // Check for a class entry + result = (Boolean)classAssertionStatus.get(className); + if (result != null) + return result.booleanValue(); + + // Check for most specific package entry + int dotIndex = className.lastIndexOf("."); + if (dotIndex < 0) { // default package + result = (Boolean)packageAssertionStatus.get(null); + if (result != null) + return result.booleanValue(); + } + while(dotIndex > 0) { + className = className.substring(0, dotIndex); + result = (Boolean)packageAssertionStatus.get(className); + if (result != null) + return result.booleanValue(); + dotIndex = className.lastIndexOf(".", dotIndex-1); + } + + // Return the classloader default + return defaultAssertionStatus; + } + + // Set up the assertions with information provided by the VM. + private void initializeJavaAssertionMaps() { + // assert Thread.holdsLock(this); + + classAssertionStatus = new HashMap(); + packageAssertionStatus = new HashMap(); + AssertionStatusDirectives directives = retrieveDirectives(); + + for(int i=0; iclasses.length; i++) + classAssertionStatus.put(directives.classes[i], + Boolean.valueOf(directives.classEnabled[i])); + + for(int i=0; ipackages.length; i++) + packageAssertionStatus.put(directives.packages[i], + Boolean.valueOf(directives.packageEnabled[i])); + + defaultAssertionStatus = directives.deflt; + } + + // Retrieves the assertion directives from the VM. + private static native AssertionStatusDirectives retrieveDirectives(); + +} + + +class SystemClassLoaderAction implements PrivilegedExceptionAction { + private ClassLoader parent; + + SystemClassLoaderAction(ClassLoader parent) { + this.parent = parent; + } + + public Object run() throws Exception { + ClassLoader sys; + Constructor ctor; + Class c; + Class cp[] = { ClassLoader.class }; + Object params[] = { parent }; + + String cls = System.getProperty("java.system.class.loader"); + if (cls == null) { + return parent; + } + + c = Class.forName(cls, true, parent); + ctor = c.getDeclaredConstructor(cp); + sys = (ClassLoader) ctor.newInstance(params); + Thread.currentThread().setContextClassLoader(sys); + return sys; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/aThinlet_hl.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/aThinlet_hl.java new file mode 100644 index 000000000000..8aca05ad4bfb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/aThinlet_hl.java @@ -0,0 +1,5393 @@ +/* + Thinlet GUI toolkit - www.thinlet.com + Copyright (C) 2002 Robert Bajzat (robert.bajzat@thinlet.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +//package thinlet; //java +//midp package thinlet.midp; + +import java.applet.*; //java +import java.awt.*; //java +import java.awt.datatransfer.*; //java +import java.awt.image.*; //java +import java.awt.event.*; //java +import java.lang.reflect.*; //java +import java.io.*; +import java.net.*; //java +import java.util.*; +//midp import javax.microedition.lcdui.*; +//midp import javax.microedition.midlet.*; + +/** + * + */ +public class Thinlet extends Container //java + implements Runnable, Serializable { //java +//midp public class Thinlet extends Canvas implements CommandListener { + + //midp private static final Boolean TRUE = new Boolean(true); + //midp private static final Boolean FALSE = new Boolean(false); + + //midp private transient Font font; + private transient Color c_bg; + private transient Color c_text; + private transient Color c_textbg; + private transient Color c_border; + private transient Color c_disable; + private transient Color c_hover; + private transient Color c_press; + private transient Color c_focus; + private transient Color c_select; + private transient Color c_ctrl = null; //java + //midp private transient Color c_ctrl; + private transient int block; + private transient Image gradient; //java + { + setFont(new Font("SansSerif", Font.PLAIN, 12)); //java + //midp setFont(Font.getDefaultFont()); + setColors(0xe6e6e6, 0x000000, 0xffffff, + 0x909090, 0xb0b0b0, 0xededed, 0xb9b9b9, 0x89899a, 0xc5c5dd); // f99237 eac16a // e68b2c ffc73c + } + + private transient Thread timer; + private transient long watchdelay; + private transient long watch; + private transient String clipboard; + + private Object content = createImpl("desktop"); + private transient Object mouseinside; + private transient Object insidepart; + private transient Object mousepressed; + private transient Object pressedpart; + private transient int referencex, referencey; + private transient int mousex, mousey; + private transient Object focusowner; + private transient boolean focusinside; //midp { focusinside = true; } + private transient Object popupowner; + private transient Object tooltipowner; + //private transient int pressedkey; + + //java> + private static final int DRAG_ENTERED = AWTEvent.RESERVED_ID_MAX + 1; + private static final int DRAG_EXITED = AWTEvent.RESERVED_ID_MAX + 2; + + private static long WHEEL_MASK = 0; + private static int MOUSE_WHEEL = 0; + private static Method wheelrotation = null; + static { + try { + WHEEL_MASK = AWTEvent.class.getField("MOUSE_WHEEL_EVENT_MASK").getLong(null); + MOUSE_WHEEL = MouseEvent.class.getField("MOUSE_WHEEL").getInt(null); + } catch (Exception exc) { /* not 1.4 */ } + } + { + if (MOUSE_WHEEL != 0) { // disable global focus-manager for this component in 1.4 + try { + getClass().getMethod("setFocusTraversalKeysEnabled", new Class[] { Boolean.TYPE }). + invoke(this, new Object[] { Boolean.FALSE }); + } catch (Exception exc) { /* never */ } + } + enableEvents(AWTEvent.COMPONENT_EVENT_MASK | + AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK | + AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | WHEEL_MASK); + } + //Color(background); c_text = new Color(text); + c_textbg = new Color(textbackground); c_border = new Color(border); + c_disable = new Color(disable); c_hover = new Color(hover); + c_press = new Color(press); c_focus = new Color(focus); + c_select = new Color(select); + //midp c_ctrl = c_hover; + //java> + int[] pix = new int[block * block]; + int r1 = c_bg.getRed(); int r2 = c_press.getRed(); + int g1 = c_bg.getGreen(); int g2 = c_press.getGreen(); + int b1 = c_bg.getBlue(); int b2 = c_press.getBlue(); + for (int i = 0; i < block; i++) { + int r = r1 - (r1 - r2) * i / block; + int g = g1 - (g1 - g2) * i / block; + int b = b1 - (b1 - b2) * i / block; + int color = (255 << 24) | (r << 16) | (g << 8) | b; + for (int j = 0; j < block; j++) { + pix[i * block + j] = color; + //pix[j * block + i] = color; + } + } + gradient = createImage(new MemoryImageSource(block, block, pix, 0, block)); + //Font font) { + block = getFontMetrics(font).getHeight(); //java + super.setFont(font); //java + //midp block = font.getHeight(); + //midp this.font = font; + } + + /** + * + */ + private void doLayout(Object component) { + String classname = getClass(component); + if ("combobox" == classname) { + if (getBoolean(component, "editable", true)) { + Image icon = getIcon(component, "icon", null); + layoutField(component, block, false, + (icon != null) ? icon.getWidth(this) : 0); + } // set editable -> validate (overwrite textfield repaint) + else { + int selected = getInteger(component, "selected", -1); + if (selected != -1) { + Object choice = getItem(component, "choice", selected); + set(component, "text", get(choice, "text")); + set(component, "icon", get(choice, "icon")); + } + } + } + else if (("textfield" == classname) || ("passwordfield" == classname)) { + layoutField(component, 0, ("passwordfield" == classname), 0); + } + else if ("textarea" == classname) { + String text = getString(component, "text", ""); + int start = getInteger(component, "start", 0); + if (start > text.length()) { setInteger(component, "start", start = text.length(), 0); } + int end = getInteger(component, "end", 0); + if (end > text.length()) { setInteger(component, "end", end = text.length(), 0); } + int caretx = 0; int carety = 0; + FontMetrics fm = getFontMetrics(getFont()); //java + int width = 0, height = 0; + for (int i = 0, j = 0; j != -1; i = j + 1) { + j = text.indexOf('\n', i); + if (i != j) { // && i != text.length() + String line = (j != -1) ? text.substring(i, j) : text.substring(i); //java + width = Math.max(width, fm.stringWidth(line)); //java + //midp width = font.substringWidth(text, i, ((j != -1) ? j : text.length()) - i); + } + if ((end >= i) && ((j == -1) || (end <= j))) { + caretx = fm.stringWidth(text.substring(i, end)); //java + //midp caretx = font.substringWidth(text, i, end - i); + carety = height; + } + height += fm.getHeight(); + } + layoutScrollPane(component, width + 2, + height - fm.getLeading() + 2, 0, 0); + scrollToVisible(component, caretx, carety, + 2, fm.getAscent() + fm.getDescent() + 2); + } + else if ("tabbedpane" == classname) { + Rectangle bounds = getRectangle(component, "bounds"); + String placement = getString(component, "placement", "top"); + boolean horizontal = ((placement == "top") || (placement == "bottom")); + int tabd = 0; + int tabsize = 0; + for (Object comp = get(component, "tab"); + comp != null; comp = get(comp, ":next")) { + Dimension d = getSize(comp, 8, 4, "left"); + setRectangle(comp, "bounds", + horizontal ? tabd : 0, horizontal ? 0 : tabd, d.width, d.height); + tabd += horizontal ? d.width : d.height; + tabsize = Math.max(tabsize, horizontal ? d.height : d.width); + } + for (Object comp = get(component, "tab"); + comp != null; comp = get(comp, ":next")) { + Rectangle r = getRectangle(comp, "bounds"); + if (horizontal) { + if (placement == "bottom") { r.y = bounds.height - tabsize; } + r.height = tabsize; + } else { + if (placement == "right") { r.x = bounds.width - tabsize; } + r.width = tabsize; + } + } + int cx = (placement == "left") ? (tabsize + 1) : 2; + int cy = (placement == "top") ? (tabsize + 1) : 2; + int cwidth = bounds.width - (horizontal ? 4 : (tabsize + 3)); + int cheight = bounds.height - (horizontal ? (tabsize + 3) : 4); + for (Object comp = get(component, "component"); + comp != null; comp = get(comp, ":next")) { + if (!getBoolean(comp, "visible", true)) { continue; } + setRectangle(comp, "bounds", cx, cy, cwidth, cheight); + doLayout(comp); + } + } + else if (("panel" == classname) || (classname == "dialog")) { + int gap = getInteger(component, "gap", 0); + int[][] grid = getGrid(component, gap); + if (grid != null) { + int top = getInteger(component, "top", 0); + int left = getInteger(component, "left", 0); + int bottom = getInteger(component, "bottom", 0); + int right = getInteger(component, "right", 0); + if (classname == "dialog") { + int titleheight = getInteger(component, "titleheight", 0); + top += 4 + titleheight; left += 4; bottom += 4; right += 4; + } + + Rectangle bounds = getRectangle(component, "bounds"); + for (int i = 0; i < 2; i++) { + int d = ((i == 0) ? (bounds.width - left - right) : + (bounds.height - top - bottom)) - + getSum(grid[i], 0, grid[i].length, gap, false); + if (d != 0) { + int w = getSum(grid[2 + i], 0, grid[2 + i].length, 0, false); + if (w > 0) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[2 + i][j] != 0) { + grid[i][j] += d * grid[2 + i][j] / w; + } + } + } + } + } + + int i = 0; + for (Object comp = get(component, "component"); + comp != null; comp = get(comp, ":next")) { + if (!getBoolean(comp, "visible", true)) { continue; } + int ix = left + getSum(grid[0], 0, grid[4][i], gap, true); + int iy = top + getSum(grid[1], 0, grid[5][i], gap, true); + int iwidth = getSum(grid[0], grid[4][i], grid[6][i], gap, false); + int iheight = getSum(grid[1], grid[5][i], grid[7][i], gap, false); + String halign = getString(comp, "halign", "fill"); + String valign = getString(comp, "valign", "fill"); + if ((halign != "fill") || (valign != "fill")) { + Dimension d = getPreferredSize(comp); + if (halign != "fill") { + int dw = Math.max(0, iwidth - d.width); + if (halign == "center") { ix += dw / 2; } + else if (halign == "right") { ix += dw; } + iwidth -= dw; + } + if (valign != "fill") { + int dh = Math.max(0, iheight - d.height); + if (valign == "center") { iy += dh / 2; } + else if (valign == "bottom") { iy += dh; } + iheight -= dh; + } + } + setRectangle(comp, "bounds", ix, iy, iwidth, iheight); + doLayout(comp); + i++; + } + } + } + else if ("desktop" == classname) { + Rectangle bounds = getRectangle(component, "bounds"); + for (Object comp = get(component, "component"); + comp != null; comp = get(comp, ":next")) { + String iclass = getClass(comp); + if (iclass == "dialog") { + Dimension d = getPreferredSize(comp); + if (get(comp, "bounds") == null) + setRectangle(comp, "bounds", + Math.max(0, (bounds.width - d.width) / 2), + Math.max(0, (bounds.height - d.height) / 2), + Math.min(d.width, bounds.width), Math.min(d.height, bounds.height)); + } else if ((iclass == "combolist") || (iclass == "popupmenu")) { + iclass = iclass; //compiler bug + } else { + setRectangle(comp, "bounds", 0, 0, bounds.width, bounds.height); + } + doLayout(comp); + } + } + else if ("spinbox" == classname) { + layoutField(component, block, false, 0); + } + else if ("splitpane" == classname) { + Rectangle bounds = getRectangle(component, "bounds"); + boolean horizontal = ("vertical" != get(component, "orientation")); + int divider = getInteger(component, "divider", -1); + int maxdiv = (horizontal ? bounds.width : bounds.height) - 5; + + Object comp1 = get(component, "component"); + boolean visible1 = (comp1 != null) && getBoolean(comp1, "visible", true); + if (divider == -1) { + int d1 = 0; + if (visible1) { + Dimension d = getPreferredSize(comp1); + d1 = horizontal ? d.width : d.height; + } + divider = Math.min(d1, maxdiv); + setInteger(component, "divider", divider, -1); + } + else if (divider > maxdiv) { + setInteger(component, "divider", divider = maxdiv, -1); + } + + if (visible1) { + setRectangle(comp1, "bounds", 0, 0, horizontal ? divider : bounds.width, + horizontal ? bounds.height : divider); + doLayout(comp1); + } + Object comp2 = (comp1 != null) ? get(comp1, ":next") : null; + if ((comp2 != null) && getBoolean(comp2, "visible", true)) { + setRectangle(comp2, "bounds", horizontal ? (divider + 5) : 0, + horizontal ? 0 : (divider + 5), + horizontal ? (bounds.width - 5 - divider) : bounds.width, + horizontal ? bounds.height : (bounds.height - 5 - divider)); + doLayout(comp2); + } + } + else if (("list" == classname) || + ("table" == classname) || ("tree" == classname)) { + int width = 0; + int columnheight = 0; + if ("table" == classname) { + for (Object column = get(component, "column"); + column != null; column = get(column, ":next")) { + width += getInteger(column, "width", 80); + Dimension d = getSize(column, 2, 2, "left"); + columnheight = Math.max(columnheight, d.height); + } + } + String itemname = ("list" == classname) ? "item" : + (("table" == classname) ? "row" : "node"); + int y = 0; + int level = 0; + for (Object item = get(component, itemname); item != null;) { + int x = 0; + int iwidth = 0; int iheight = 0; + if ("table" == classname) { + iwidth = width; + for (Object cell = get(item, "cell"); + cell != null; cell = get(cell, ":next")) { + Dimension d = getSize(cell, 2, 3, "left"); + iheight = Math.max(iheight, d.height); + } + } + else { + if ("tree" == classname) { + x = (level + 1) * block; + } + Dimension d = getSize(item, 2, 3, "left"); + iwidth = d.width; iheight = d.height; + width = Math.max(width, x + d.width); + } + setRectangle(item, "bounds", x, y, iwidth, iheight); + y += iheight; + if ("tree" == classname) { + Object next = get(item, "node"); + if ((next != null) && getBoolean(item, "expanded", true)) { + level++; + } else { + while (((next = get(item, ":next")) == null) && (level > 0)) { + item = getParent(item); + level--; + } + } + item = next; + } else { + item = get(item, ":next"); + } + } + layoutScrollPane(component, width, y - 1, 0, columnheight); + } + else if ("menubar" == classname) { + Rectangle bounds = getRectangle(component, "bounds"); + int x = 0; + for (Object menu = get(component, "menu"); + menu != null; menu = get(menu, ":next")) { + Dimension d = getSize(menu, 8, 4, "left"); + setRectangle(menu, "bounds", x, 0, d.width, bounds.height); + x += d.width; + } + } + else if (("combolist" == classname) || ("popupmenu" == classname)) { + boolean combo = ("combolist" == classname); + int pw = 0; int ph = 0; int pxy = combo ? 0 : 1; + for (Object item = get(get(component, combo ? "combobox" : "menu"), + combo ? "choice" : "menu"); item != null; item = get(item, ":next")) { + String itemclass = combo ? null : getClass(item); + Dimension d = (itemclass == "separator") ? new Dimension(1, 1) : + getSize(item, 8 , 4, "left"); + if (itemclass == "checkboxmenuitem") { + d.width = d.width + block + 3; + d.height = Math.max(block, d.height); + } + else if (itemclass == "menu") { + d.width += block; + } + setRectangle(item, "bounds", pxy, pxy + ph, d.width, d.height); + pw = Math.max(pw, d.width); + ph += d.height; + } + Rectangle r = getRectangle(component, "bounds"); + r.width = pw + 2; r.height = ph + 2; + if (combo) { + Rectangle db = getRectangle(content, "bounds"); + if (r.y + ph + 2 > db.height) { + r.width = pw + 2 + block; + r.height = db.height - r.y; + } + else { + r.height = Math.min(r.height, db.height - r.y); + } + r.width = Math.min(r.width, db.width - r.x); + layoutScrollPane(component, pw, ph, 0, 0);//~ + } + } + //java> + else if ("bean" == classname) { + Rectangle r = getRectangle(component, "bounds"); + ((Component) get(component, "bean")).setBounds(r); + } + //MouseEvent.MOUSE_ENTERED, mouseinside, insidepart); //java + } + popup(popupmenu, "popupmenu"); + } + if ((selected == null) || (getClass(selected) != "menu")) { return null; } + popup = createImpl("popupmenu"); + set(popup, "menu", selected); + set(component, "popupmenu", popup); + + Rectangle bounds = getRectangle(selected, "bounds"); + if ("menubar" == classname) { + px = bounds.x; py = bounds.y + bounds.height - 1; + } else { + px = bounds.x + getRectangle(component, "bounds").width - 4; + py = bounds.y; + } + } + else { //if ("combobox" == classname) { + popup = createImpl("combolist"); + set(popup, "combobox", component); + set(component, "combolist", popup); + + py = getRectangle(component, "bounds").height + 1; + } + if (("menubar" == classname) || ("combobox" == classname)) { + popupowner = component; + } + insertItem(content, "component", popup, 0); + set(popup, ":parent", content); + while (component != content) { + Rectangle r = getRectangle(component, "bounds"); + px += r.x; py += r.y; + component = getParent(component); + } + setRectangle(popup, "bounds", px, py, 0, 0); + doLayout(popup); repaint(popup); + return popup; + } + + /** + * + */ + private void closeup(Object combobox, Object combolist, Object item) { + if ((item != null) && getBoolean(item, "enabled", true)) { + String text = getString(item, "text", ""); + set(combobox, "text", text); // if editable + setInteger(combobox, "start", text.length(), 0); + setInteger(combobox, "end", 0, 0); + set(combobox, "icon", get(item, "icon")); + validate(combobox); + setInteger(combobox, "selected", getIndex(combobox, "choice", item), -1); + invoke(combobox, "action"); + } + set(combolist, "combobox", null); + set(combobox, "combolist", null); + removeItemImpl(content, "component", combolist); + repaint(combolist); + set(combolist, ":parent", null); + popupowner = null; + if (mouseinside == combolist) { + findComponent(content, mousex, mousey); + handleMouseEvent(mousex, mousex, 1, false, false, false, //java + MouseEvent.MOUSE_ENTERED, mouseinside, insidepart); //java + } + } + + /** + * + */ + private void closeup(Object menubar) { + set(menubar, "selected", null); + popup(menubar, "menubar"); + repaint(menubar); // , selected + popupowner = null; + } + + /** + * + */ + private void closeup() { + if (popupowner != null) { + String classname = getClass(popupowner); + if ("menubar" == classname) { + closeup(popupowner); + } + else if ("combobox" == classname) { + closeup(popupowner, get(popupowner, "combolist"), null); + } + } + } + + /** + * + */ + private void showTip() { + String text = null; + tooltipowner = null; + String classname = getClass(mouseinside); + if ((classname == "tabbedpane") || (classname == "menubar") || (classname == "popupmenu")) { + if (insidepart != null) { + text = getString(insidepart, "tooltip", null); + } + } + else if (classname == "combolist") { + if (insidepart instanceof Object[]) { + text = getString(insidepart, "tooltip", null); + } + } + //list table tree + if (text == null) { text = getString(mouseinside, "tooltip", null); } + else { tooltipowner = insidepart; } + if (text != null) { + FontMetrics fm = getFontMetrics(getFont()); + int width = fm.stringWidth(text) + 4; + int height = fm.getAscent() + fm.getDescent() + 4; + if (tooltipowner == null) { tooltipowner = mouseinside; } + setRectangle(tooltipowner, "tooltipbounds", mousex + 10, mousey + 10, width, height); + repaint(mousex + 10, mousey + 10, width, height); + } + } + + /** + * + */ + private void hideTip() { + if (tooltipowner != null) { + Rectangle bounds = getRectangle(tooltipowner, "tooltipbounds"); + set(tooltipowner, "tooltipbounds", null); + tooltipowner = null; + repaint(bounds.x, bounds.y, bounds.width, bounds.height); + } + } + + /** + * + */ + private void layoutField(Object component, int dw, boolean hidden, int left) { + int width = getRectangle(component, "bounds").width - left -dw; + String text = getString(component, "text", ""); + int start = getInteger(component, "start", 0); + if (start > text.length()) { setInteger(component, "start", start = text.length(), 0); } + int end = getInteger(component, "end", 0); + if (end > text.length()) { setInteger(component, "end", end = text.length(), 0); } + int offset = getInteger(component, "offset", 0); + int off = offset; + FontMetrics fm = getFontMetrics(getFont()); + int caret = hidden ? (fm.charWidth('*') * end) : + fm.stringWidth(text.substring(0, end)); //java + //midp font.substringWidth(text, 0, end); + if (off > caret) { + off = caret; + } + else if (off < caret - width + 4) { + off = caret - width + 4; + } + off = Math.max(0, Math.min(off, (hidden ? (fm.charWidth('*') * + text.length()) : fm.stringWidth(text)) - width + 4)); + if (off != offset) { + setInteger(component, "offset", off, 0); + } + } + + /** + * + */ + private void layoutScrollPane(Object component, + int contentwidth, int contentheight, int rowwidth, int columnheight) { + Rectangle bounds = getRectangle(component, "bounds"); + boolean hneed = false; boolean vneed = false; + if (contentwidth > bounds.width - rowwidth - 2) { + hneed = true; + vneed = (contentheight > bounds.height - columnheight - 2 - block); + } + if (vneed || (contentheight > bounds.height - columnheight - 2)) { + vneed = true; + hneed = hneed || (contentwidth > bounds.width - rowwidth - 2 - block); + } + int viewportwidth = bounds.width - rowwidth - (vneed ? block : 0); + int viewportheight = bounds.height - columnheight - (hneed ? block : 0); + setRectangle(component, ":port", + rowwidth, columnheight, viewportwidth, viewportheight); //?rowwidth + + Rectangle view = getRectangle(component, ":view"); + setRectangle(component, ":view", + (view != null) ? Math.max(0, + Math.min(view.x, contentwidth - viewportwidth + 2)) : 0, + (view != null) ? Math.max(0, + Math.min(view.y, contentheight - viewportheight + 2)) : 0, + Math.max(viewportwidth - 2, contentwidth), + Math.max(viewportheight - 2, contentheight)); + } + + /** + * + */ + private void scrollToVisible(Object component, + int x, int y, int width, int height) { + Rectangle view = getRectangle(component, ":view"); + Rectangle port = getRectangle(component, ":port"); + int vx = Math.max(x + width - port.width + 2, Math.min(view.x, x)); + int vy = Math.max(y + height - port.height + 2, Math.min(view.y, y)); + if ((view.x != vx) || (view.y != vy)) { + repaint(component); // horizontal | vertical + view.x = vx; view.y = vy; + } + } + + /** + * + */ + public Dimension getPreferredSize() { + return getPreferredSize(content); + } + + /** + * + */ + private Dimension getPreferredSize(Object component) { + int width = getInteger(component, "width", 0); + int height = getInteger(component, "height", 0); + if ((width > 0) && (height > 0)) { + return new Dimension(width, height); + } + String classname = getClass(component); + //System.out.println("classname: " + classname); + if ("label" == classname) { + return getSize(component, 0, 0, "left"); + } + if ("button" == classname) { + return getSize(component, 12, 6, "center"); + } + if ("checkbox" == classname) { + Dimension d = getSize(component, 0, 0, "left"); + d.width = d.width + block + 3; + d.height = Math.max(block, d.height); + return d; + } + if ("combobox" == classname) { + if (getBoolean(component, "editable", true)) { + Dimension size = getFieldSize(component); + Image icon = getIcon(component, "icon", null); + if (icon != null) { + size.width += icon.getWidth(this); + size.height = Math.max(size.height, icon.getHeight(this) + 2); + } + size.width += block; + return size; + } else { + int selected = getInteger(component, "selected", -1); + return getSize((selected != -1) ? + getItemImpl(component, "choice", selected) : + get(component, "choice"), 4 + block, 4, "left"); + } + } + if (("textfield" == classname) || ("passwordfield" == classname)) { + return getFieldSize(component); + } + if ("textarea" == classname) { + int columns = getInteger(component, "columns", 0); + int rows = getInteger(component, "rows", 0); // 'e' -> 'm' ? + FontMetrics fm = getFontMetrics(getFont()); //java + return new Dimension( + ((columns > 0) ? (columns * fm.charWidth('e') + 2) : 76) + 2 + block, + ((rows > 0) ? (rows * fm.getHeight() - fm.getLeading() + 2) : 76) + 2 + block); + } + if ("tabbedpane" == classname) { + String placement = getString(component, "placement", "top"); + boolean horizontal = ((placement == "top") || (placement == "bottom")); + int tabsize = 0; + int contentwidth = 0; int contentheight = 0; + for (Object comp = get(component, "tab"); + comp != null; comp = get(comp, ":next")) { + Dimension d = getSize(comp, 8, 4, "left"); + tabsize = Math.max(tabsize, horizontal ? d.height : d.width); + } + for (Object comp = get(component, "component"); + comp != null; comp = get(comp, ":next")) { + if (!getBoolean(comp, "visible", true)) { continue; } + Dimension d = getPreferredSize(comp); + contentwidth = Math.max(contentwidth, d.width); + contentheight = Math.max(contentheight, d.height); + } + return new Dimension(contentwidth + (horizontal ? 4 : (tabsize + 3)), + contentheight + (horizontal ? (tabsize + 3) : 4)); + } + if (("panel" == classname) || (classname == "dialog")) { + Dimension size = new Dimension( + getInteger(component, "left", 0) + getInteger(component, "right", 0), + getInteger(component, "top", 0) + getInteger(component, "bottom", 0)); + if (classname == "dialog") { + int titleheight = getSize(component, 0, 0, "left").height; + setInteger(component, "titleheight", titleheight, 0); + size.width += 8; size.height += 8 + titleheight; + } + int gap = getInteger(component, "gap", 0); + int[][] grid = getGrid(component, gap); + if (grid != null) { + size.width += getSum(grid[0], 0, grid[0].length, gap, false); + size.height += getSum(grid[1], 0, grid[1].length, gap, false); + } + return size; + } + else if ("desktop" == classname) { + Dimension size = new Dimension(); + for (Object comp = get(component, "component"); + comp != null; comp = get(comp, ":next")) { + String iclass = getClass(comp); + if ((iclass != "dialog") && (iclass != "popupmenu") && + (iclass != "combolist")) { + Dimension d = getPreferredSize(comp); + size.width = Math.max(d.width, size.width); + size.height = Math.max(d.height, size.height); + } + } + return size; + } + if ("spinbox" == classname) { + Dimension size = getFieldSize(component); + size.width += block; + return size; + } + if ("progressbar" == classname) { + boolean horizontal = ("vertical" != get(component, "orientation")); + return new Dimension(horizontal ? 76 : 6, horizontal ? 6 : 76); + } + if ("slider" == classname) { + boolean horizontal = ("vertical" != get(component, "orientation")); + return new Dimension(horizontal ? 76 : 10, horizontal ? 10 : 76); + } + if ("splitpane" == classname) { + boolean horizontal = ("vertical" != get(component, "orientation")); + Object comp1 = get(component, "component"); + Dimension size = ((comp1 == null) || !getBoolean(comp1, "visible", true)) ? + new Dimension() : getPreferredSize(comp1); + Object comp2 = get(comp1, ":next"); + if ((comp2 != null) && getBoolean(comp2, "visible", true)) { + Dimension d = getPreferredSize(comp2); + size.width = horizontal ? (size.width + d.width) : + Math.max(size.width, d.width); + size.height = horizontal ? Math.max(size.height, d.height) : + (size.height + d.height); + } + if (horizontal) { size.width += 5; } else { size.height += 5; } + return size; + } + if (("list" == classname) || + ("table" == classname) || ("tree" == classname)) { + return new Dimension(76 + 2 + block, 76 + 2 + block); + } + if ("separator" == classname) { + return new Dimension(1, 1); + } + if ("menubar" == classname) { + Dimension size = new Dimension(0, 0); + for (Object menu = get(component, "menu"); + menu != null; menu = get(menu, ":next")) { + Dimension d = getSize(menu, 8, 4, "left"); + size.width += d.width; + size.height = Math.max(size.height, d.height); + } + return size; + } + //java> + if ("bean" == classname) { + return ((Component) get(component, "bean")).getPreferredSize(); + } + // columns)) { + x = 0; y++; j = -1; + } + else if (columnheight[x + j] > y) { + x += (j + 1); j = -1; + } + } + if (y + rowspan > grid[1].length) { + int[] rowheights = new int[y + rowspan]; + System.arraycopy(grid[1], 0, rowheights, 0, grid[1].length); + grid[1] = rowheights; + int[] rowweights = new int[y + rowspan]; + System.arraycopy(grid[3], 0, rowweights, 0, grid[3].length); + grid[3] = rowweights; + } + for (int j = 0; j < colspan; j++) { + columnheight[x + j] = y + rowspan; + } + + int weightx = getInteger(comp, "weightx", 0); + int weighty = getInteger(comp, "weighty", 0); + Dimension d = getPreferredSize(comp); + + if (colspan == 1) { + grid[0][x] = Math.max(grid[0][x], d.width); // columnwidths + grid[2][x] = Math.max(grid[2][x], weightx); // columnweights + } + else { + if (cache == null) { cache = new int[4][count]; } + cache[0][i] = d.width; + cache[2][i] = weightx; + if ((nextsize == 0) || (colspan < nextsize)) { nextsize = colspan; } + } + if (rowspan == 1) { + grid[1][y] = Math.max(grid[1][y], d.height); // rowheights + grid[3][y] = Math.max(grid[3][y], weighty); // rowweights + } + else { + if (cache == null) { cache = new int[4][count]; } + cache[1][i] = d.height; + cache[3][i] = weighty; + if ((nextsize == 0) || (rowspan < nextsize)) { nextsize = rowspan; } + } + grid[4][i] = x; //gridx + grid[5][i] = y; //gridy + grid[6][i] = colspan; //gridwidth + grid[7][i] = rowspan; //gridheight + + x += colspan; + i++; + } + + while (nextsize != 0) { + int size = nextsize; nextsize = 0; + for (int j = 0; j < 2; j++) { // horizontal, vertical + for (int k = 0; k < count; k++) { + if (grid[6 + j][k] == size) { // gridwidth, gridheight + int gridpoint = grid[4 + j][k]; // gridx, gridy + + int weightdiff = cache[2 + j][k]; + for (int m = 0; (weightdiff > 0) && (m < size); m++) { + weightdiff -= grid[2 + j][gridpoint + m]; + } + if (weightdiff > 0) { + int weightsum = cache[2 + j][k] - weightdiff; + for (int m = 0; (weightsum > 0) && (m < size); m++) { + int weight = grid[2 + j][gridpoint + m]; + if (weight > 0) { + int weightinc = weight * weightdiff / weightsum; + grid[2 + j][gridpoint + m] += weightinc; + weightdiff -= weightinc; + weightsum -= weightinc; + } + } + grid[2 + j][gridpoint + size - 1] += weightdiff; + } + + int sizediff = cache[j][k]; + int weightsum = 0; + for (int m = 0; (sizediff > 0) && (m < size); m++) { + sizediff -= grid[j][gridpoint + m]; + weightsum += grid[2 + j][gridpoint + m]; + } + if (sizediff > 0) { + for (int m = 0; (weightsum > 0) && (m < size); m++) { + int weight = grid[2 + j][gridpoint + m]; + if (weight > 0) { + int sizeinc = weight * sizediff / weightsum; + grid[j][gridpoint + m] += sizeinc; + sizediff -= sizeinc; + weightsum -= weight; + } + } + grid[j][gridpoint + size - 1] += sizediff; + } + } + else if ((grid[6 + j][k] > size) && + ((nextsize == 0) || (grid[6 + j][k] < nextsize))) { + nextsize = grid[6 + j][k]; + } + } + } + } + return grid; + } + + /** + * + */ + private int getSum(int[] values, + int from, int length, int gap, boolean last) { + if (length <= 0) { return 0; } + int value = 0; + for (int i = 0; i < length; i++) { + value += values[from + i]; + } + return value + (length - (last ? 0 : 1)) * gap; + } + + /** + * + */ + private Dimension getFieldSize(Object component) { + String text = getString(component, "text", ""); + int columns = getInteger(component, "columns", 0); + FontMetrics fm = getFontMetrics(getFont()); + return new Dimension(((columns > 0) ? + (columns * fm.charWidth('e')) : 76) + 4, + fm.getAscent() + fm.getDescent() + 4); // fm.stringWidth(text) + } + + /** + * + */ + private Dimension getSize(Object component, + int dx, int dy, String defaultalignment) { + String text = getString(component, "text", null); + int tw = 0; int th = 0; + if (text != null) { + FontMetrics fm = getFontMetrics(getFont()); + tw = fm.stringWidth(text); + th = fm.getAscent() + fm.getDescent(); + } + Image icon = getIcon(component, "icon", null); + int iw = 0; int ih = 0; + if (icon != null) { + iw = icon.getWidth(this); + ih = icon.getHeight(this); + } + return new Dimension(tw + iw + dx, Math.max(th, ih) + dy); + } + //java> + + /** + * + */ + public void update(Graphics g) { + paint(g); + } + + /** + *~ + */ + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { + if (infoflags == ImageObserver.ALLBITS) { + validate(content); + return super.imageUpdate(img, infoflags, x, y, width, height); + } + return true; + } + + /** + * + */ + public void paint(Graphics g) { + //g.setColor(Color.orange); + //g.fillRect(0, 0, getSize().width, getSize().height); + //long time = System.currentTimeMillis(); + Rectangle clip = g.getClipBounds(); + ///dg.setClip(r.x, r.y, r.width, r.height); + paint(g, clip.x, clip.y, clip.width, clip.height, content, isEnabled()); + //System.out.println(System.currentTimeMillis() - time); + ///g.setClip(0, 0, getSize().width, getSize().height); + //g.setColor(Color.red); g.drawRect(clip.x, clip.y, clip.width - 1, clip.height - 1); + } + + //Graphics g, + int clipx, int clipy, int clipwidth, int clipheight, + Object component, boolean enabled) { + if (!getBoolean(component, "visible", true)) { return; } + Rectangle bounds = getRectangle(component, "bounds"); + if (bounds == null) { return; } + if (bounds.width < 0) { + bounds.width = Math.abs(bounds.width); + doLayout(component); + } + if ((clipx + clipwidth < bounds.x) || + (clipx > bounds.x + bounds.width) || + (clipy + clipheight < bounds.y) || + (clipy > bounds.y + bounds.height)) { + return; + } + clipx -= bounds.x; clipy -= bounds.y; + String classname = getClass(component); + boolean pressed = (mousepressed == component); + boolean inside = (mouseinside == component) && + ((mousepressed == null) || pressed); + boolean focus = focusinside && (focusowner == component); + enabled = getBoolean(component, "enabled", true); //enabled && + g.translate(bounds.x, bounds.y); + //g.setClip(0, 0, bounds.width, bounds.height); + + if ("label" == classname) { + paintContent(component, g, clipx, clipy, clipwidth, clipheight, + 0, 0, bounds.width, bounds.height, + enabled ? c_text : c_disable, "left", true); + } + else if ("button" == classname) { + paintRect(g, 0, 0, bounds.width, bounds.height, + enabled ? c_border : c_disable, + enabled ? ((inside != pressed) ? c_hover : + (pressed ? c_press : c_ctrl)) : c_bg, true, true, true, true); + if (focus) { + g.setColor(c_focus); + g.drawRect(2, 2, bounds.width - 5, bounds.height - 5); + } + paintContent(component, g, clipx, clipy, clipwidth, clipheight, + 6, 3, bounds.width - 12, bounds.height - 6, + enabled ? c_text : c_disable, "center", true); + } + else if ("checkbox" == classname) { + boolean selected = getBoolean(component, "selected", false); + String group = getString(component, "group", null); + Color border = enabled ? c_border : c_disable; + Color foreground = enabled ? ((inside != pressed) ? c_hover : + (pressed ? c_press : c_ctrl)) : c_bg; + int dy = (bounds.height - block + 2) / 2; + if (group == null) { + paintRect(g, 1, dy + 1, block - 2, block - 2, + border, foreground, true, true, true, true); + } else { + g.setColor((foreground != c_ctrl) ? foreground : c_bg); + g.fillOval(1, dy + 1, block - 3, block - 3); //java + g.setColor(border); + g.drawOval(1, dy + 1, block - 3, block - 3); //java + } + if (focus) { + g.setColor(c_focus); + if (group == null) { + g.drawRect(3, dy + 3, block - 7, block - 7); + } else { + g.drawOval(3, dy + 3, block - 7, block - 7); //java + } + } + if((!selected && inside && pressed) || + (selected && (!inside || !pressed))) { + g.setColor(enabled ? c_text : c_disable); + if (group == null) { + g.fillRect(3, dy + block - 9, 2, 6); + g.drawLine(3, dy + block - 4, block - 4, dy + 3); + g.drawLine(4, dy + block - 4, block - 4, dy + 4); + } else { + g.fillOval(5, dy + 5, block - 10, block - 10); //java + g.drawOval(4, dy + 4, block - 9, block - 9); //java + } + } + paintContent(component, g, clipx, clipy, clipwidth, clipheight, + block + 3, 0, bounds.width - block - 3, bounds.height, + enabled ? c_text : c_disable, "left", true); + } + else if ("combobox" == classname) { + if (getBoolean(component, "editable", true)) { + Image icon = getIcon(component, "icon", null); + int left = (icon != null) ? icon.getWidth(this) : 0; + paintField(g, clipx, clipy, clipwidth, clipheight, component, + bounds.width - block, bounds.height, + inside, pressed, focus, enabled, false, left); + if (icon != null) { + g.drawImage(icon, 2, (bounds.height - icon.getHeight(this)) / 2, this); //java + //midp g.drawImage(icon, 2, bounds.height / 2, Graphics.LEFT | Graphics.VCENTER); + } + paintArrow(g, bounds.width - block, 0, block, bounds.height, + 'S', enabled, inside, pressed, "down", true, false, true, true); + } else { + paintRect(g, 0, 0, bounds.width, bounds.height, + enabled ? c_border : c_disable, + enabled ? ((inside != pressed) ? c_hover : + (pressed ? c_press : c_ctrl)) : c_bg, true, true, true, true); + paintContent(component, g, clipx, clipy, clipwidth, clipheight, + 2, 2, bounds.width - block - 4, bounds.height - 4, + enabled ? c_text : c_disable, "left", false); + paintArrow(g, bounds.width - block, 0, block, bounds.height, 'S'); + if (focus) { + g.setColor(c_focus); + g.drawRect(2, 2, bounds.width - block - 5, bounds.height - 5); + } + } + } + else if ("combolist" == classname) { + Rectangle view = getRectangle(component, ":view"); + Rectangle viewport = getRectangle(component, ":port"); + g.setColor(c_border); + g.drawRect(viewport.x, viewport.y, viewport.width - 1, viewport.height - 1); + if (paintScrollPane(g, clipx, clipy, clipwidth, clipheight, + bounds, view, viewport, enabled, inside, pressed)) { + Object selected = get(component, "inside"); + int ly = clipy - viewport.y - 1; + int yfrom = view.y + Math.max(0, ly); + int yto = view.y + Math.min(viewport.height - 2, ly + clipheight); + for (Object choice = get(get(component, "combobox"), "choice"); + choice != null; choice = get(choice, ":next")) { + Rectangle r = getRectangle(choice, "bounds"); + if (yto <= r.y) { break; } + if (yfrom >= r.y + r.height) { continue; } + boolean armed = (selected == choice); + paintRect(g, r.x, r.y, bounds.width - 2, r.height, c_border, + armed ? c_select : c_bg, false, false, false, false); + paintContent(choice, g, clipx, yfrom, clipwidth, yto - yfrom, + r.x + 4, r.y + 2, bounds.width - 10, r.height - 4, + getBoolean(choice, "enabled", true) ? c_text : c_disable, "left", false); + } + resetScrollPane(g, clipx, clipy, clipwidth, clipheight, view, viewport); + } + //paintRect(g, 0, 0, bounds.width, bounds.height, + // secondary1, c_ctrl, true, true, true, true); + } + else if (("textfield" == classname) || ("passwordfield" == classname)) { + paintField(g, clipx, clipy, clipwidth, clipheight, component, + bounds.width, bounds.height, + inside, pressed, focus, enabled, ("passwordfield" == classname), 0); + } + else if ("textarea" == classname) { + Rectangle view = getRectangle(component, ":view"); + Rectangle viewport = getRectangle(component, ":port"); + boolean editable = getBoolean(component, "editable", true); + paintRect(g, viewport.x, viewport.y, viewport.width, viewport.height, + enabled ? c_border : c_disable, editable ? c_textbg : c_bg, + true, true, true, true); + if (paintScrollPane(g, clipx, clipy, clipwidth, clipheight, + bounds, view, viewport, enabled, inside, pressed)) { + String text = getString(component, "text", ""); + int start = focus ? getInteger(component, "start", 0) : 0; + int end = focus ? getInteger(component, "end", 0) : 0; + int is = Math.min(start, end); int ie = Math.max(start, end); + boolean wrap = getBoolean(component, "wrap", false); + FontMetrics fm = g.getFontMetrics(); //java + int fontascent = fm.getAscent(); int fontheight = fm.getHeight(); //java + //midp int fontheight = fm.getHeight(); + int ascent = 1; + int ly = clipy - viewport.y - 1; + int yfrom = view.y + Math.max(0, ly); + int yto = view.y + Math.min(viewport.height - 2, ly + clipheight); + //g.setColor(Color.pink); g.fillRect(0, yfrom - 1, 75, 2); g.fillRect(0, yto - 1, 75, 2); + + boolean prevletter = false; int n = text.length(); char c = 0; + for (int i = 0, j = -1, k = 0; k <= n; k++) { // j is the last space index (before k) + if (yto <= ascent) { break; } + if (wrap) { + if (((k == n) || ((c = text.charAt(k)) == '\n') || (c == ' ')) && + (j > i) && (fm.stringWidth(text.substring(i, k)) > viewport.width - 4)) { + k--; // draw line to the begin of the current word (+ spaces) if it is out of width + } + else if ((k == n) || (c == '\n')) { // draw line to the text/line end + j = k; prevletter = false; + } + else { + if ((c == ' ') && (prevletter || (j > i))) { j = k; } // keep spaces starting the line + prevletter = (c != ' '); + continue; + } + } + else { + if ((k == n) || ((c = text.charAt(k)) == '\n')) { j = k; } else { continue; } + } + if (yfrom < ascent + fontheight) { + String line = (j != -1) ? text.substring(i, j) : text.substring(i); //java + if (focus && (is != ie) && (ie >= i) && ((j == -1) || (is <= j))) { + int xs = (is < i) ? -1 : (((j != -1) && (is > j)) ? (view.width - 1) : + fm.stringWidth(text.substring(i, is))); //java + //midp font.substringWidth(text, i, is - i)); + int xe = ((j != -1) && (ie > j)) ? (view.width - 1) : + fm.stringWidth(text.substring(i, ie)); //java + //midp font.substringWidth(text, i, ie - i); + g.setColor(c_select); + g.fillRect(1 + xs, ascent, xe - xs, fontheight); + } + g.setColor(enabled ? c_text : c_disable); + g.drawString(line, 1, ascent + fontascent); //java + //midp g.drawSubstring(text, i, ((j != -1) ? j : text.length()) - i, 1, ascent, Graphics.LEFT | Graphics.TOP); + if (focus && (end >= i) && ((j == -1) || (end <= j))) { + int caret = fm.stringWidth(text.substring(i, end)); //java + //midp int caret = font.substringWidth(text, i, end - i); + g.setColor(c_focus); + g.fillRect(caret, ascent, 1, fontheight); + } + } + ascent += fontheight; + i = j + 1; + } + resetScrollPane(g, clipx, clipy, clipwidth, clipheight, view, viewport); + } + } + else if ("tabbedpane" == classname) { + int i = 0; Rectangle last = null; + int selected = getInteger(component, "selected", 0); + String placement = getString(component, "placement", "top"); + for (Object comp = get(component, "tab"); + comp != null; comp = get(comp, ":next")) { + Rectangle r = getRectangle(comp, "bounds"); + boolean hover = !(selected == i) && inside && + (mousepressed == null) && (insidepart == comp); + boolean sel = (selected == i); + boolean tabenabled = enabled && getBoolean(comp, "enabled", true); + paintRect(g, r.x, r.y, r.width, r.height, + enabled ? c_border : c_disable, + tabenabled ? (sel ? c_bg : (hover ? c_hover : c_ctrl)) : c_ctrl, + (placement != "bottom") || !sel, (placement != "right") || !sel, + (placement == "bottom") || ((placement == "top") && !sel), + (placement == "right") || ((placement == "left") && !sel)); + if (focus && sel) { + g.setColor(c_focus); + g.drawRect(r.x + 2, r.y + 2, r.width - 4, r.height - 4); + } + paintContent(comp, g, clipx, clipy, clipwidth, clipheight, + r.x + 4, r.y + 2, r.width - 8, r.height - 4, + tabenabled ? c_text : c_disable, "left", true); + i++; last = r; + } + if (last != null) { + boolean horizontal = ((placement == "top") || (placement == "bottom")); + paintRect(g, horizontal ? (last.x + last.width) : last.x, + horizontal ? last.y : (last.y + last.height), + horizontal ? (bounds.width - last.x - last.width) : last.width, + horizontal ? last.height : (bounds.height - last.y - last.height), + enabled ? c_border : c_disable, c_bg, + (placement != "top"), (placement != "left"), + (placement == "top"), (placement == "left")); + paintRect(g, (placement == "left") ? last.width : 0, + (placement == "top") ? last.height : 0, + horizontal ? bounds.width : (bounds.width - last.width), + horizontal ? (bounds.height - last.height) : bounds.height, + enabled ? c_border : c_disable, c_bg, + (placement != "top"), (placement != "left"), + (placement != "bottom"), (placement != "right")); + } + Object tabcontent = getItemImpl(component, "component", selected); + if (tabcontent != null) { + paint(g, clipx, clipy, clipwidth, clipheight, tabcontent, enabled); + } + } + else if (("panel" == classname) || ("dialog" == classname)) { + if ("dialog" == classname) { + int titleheight = getInteger(component, "titleheight", 0); + paintRect(g, 0, 0, bounds.width, 3 + titleheight, + c_border, c_ctrl, true, true, false, true); + paintRect(g, 0, 3 + titleheight, bounds.width, bounds.height - 3 - titleheight, + c_border, c_press, false, true, true, true); + paintContent(component, g, clipx, clipy, clipwidth, clipheight, + 3, 2, bounds.width - 6, titleheight, c_text, "left", false); + paintRect(g, 3, 3 + titleheight, bounds.width - 6, bounds.height - 6 - titleheight, + c_border, c_bg, true, true, true, true); + } else { + paintRect(g, 0, 0, bounds.width, bounds.height, + c_border, c_bg, false, false, false, false); + } + for (Object comp = get(component, "component"); + comp != null; comp = get(comp, ":next")) { + paint(g, clipx, clipy, clipwidth, clipheight, comp, enabled); + } + } + else if ("desktop" == classname) { + paintReverse(g, clipx, clipy, clipwidth, clipheight, + get(component, "component"), enabled); + //g.setColor(Color.red); if (clip != null) g.drawRect(clipx, clipy, clipwidth, clipheight); + if (tooltipowner != null) { + Rectangle r = getRectangle(tooltipowner, "tooltipbounds"); + paintRect(g, r.x, r.y, r.width, r.height, + c_border, c_bg, true, true, true, true); + String text = getString(tooltipowner, "tooltip", null); + g.setColor(c_text); + g.drawString(text, r.x + 2, r.y + g.getFontMetrics().getAscent() + 2); //java + //midp g.drawString(text, r.x + 2, r.y + (r.height - font.getHeight()) / 2, Graphics.LEFT | Graphics.TOP); + } + } + else if ("spinbox" == classname) { + paintField(g, clipx, clipy, clipwidth, clipheight, component, + bounds.width - block, bounds.height, + inside, pressed, focus, enabled, false, 0); + paintArrow(g, bounds.width - block, 0, block, bounds.height / 2, + 'N', enabled, inside, pressed, "up", true, false, false, true); + paintArrow(g, bounds.width - block, bounds.height / 2, + block, bounds.height - (bounds.height / 2), + 'S', enabled, inside, pressed, "down", true, false, true, true); + } + else if ("progressbar" == classname) { + int minimum = getInteger(component, "minimum", 0); + int maximum = getInteger(component, "maximum", 100); + int value = getInteger(component, "value", 0); + boolean horizontal = ("vertical" != get(component, "orientation")); + int length = (value - minimum) * + ((horizontal ? bounds.width : bounds.height) - 1) / (maximum - minimum); + paintRect(g, 0, 0, horizontal ? length : bounds.width, + horizontal ? bounds.height : length, enabled ? c_border : c_disable, + c_select, true, true, horizontal, !horizontal); + paintRect(g, horizontal ? length : 0, horizontal ? 0 : length, + horizontal ? (bounds.width - length) : bounds.width , + horizontal ? bounds.height : (bounds.height - length), + enabled ? c_border : c_disable, c_bg, true, true, true, true); + } + else if ("slider" == classname) { + int minimum = getInteger(component, "minimum", 0); + int maximum = getInteger(component, "maximum", 100); + int value = getInteger(component, "value", 0); + boolean horizontal = ("vertical" != get(component, "orientation")); + int length = (value - minimum) * + ((horizontal ? bounds.width : bounds.height) - block) / + (maximum - minimum); + paintRect(g, horizontal ? 0 : 3, horizontal ? 3 : 0, + horizontal ? length : (bounds.width - 6), + horizontal ? (bounds.height - 6) : length, + enabled ? c_border : c_disable, + c_bg, true, true, horizontal, !horizontal); + paintRect(g, horizontal ? length : 0, horizontal ? 0 : length, + horizontal ? block : bounds.width, horizontal ? bounds.height : block, + enabled ? c_border : c_disable, + enabled ? c_ctrl : c_bg, true, true, true, true); + if (focus) { + g.setColor(c_focus); + g.drawRect(horizontal ? (length + 2) : 2, horizontal ? 2 : (length + 2), + (horizontal ? block : bounds.width) - 5, + (horizontal ? bounds.height : block) - 5); + //g.drawRect(length + 1, 1, block - 3, bounds.height - 3); + } + paintRect(g, horizontal ? (block + length) : 3, + horizontal ? 3 : (block + length), + bounds.width - (horizontal ? (block + length) : 6), + bounds.height - (horizontal ? 6 : (block + length)), + enabled ? c_border : c_disable, + c_bg, horizontal, !horizontal, true, true); + } + else if ("splitpane" == classname) { + boolean horizontal = ("vertical" != get(component, "orientation")); + int divider = getInteger(component, "divider", -1); + paintRect(g, horizontal ? divider : 0, horizontal ? 0 : divider, + horizontal ? 5 : bounds.width, horizontal ? bounds.height : 5, + c_border, c_bg, false, false, false, false); + g.setColor(enabled ? (focus ? c_focus : c_border) : c_disable); + int xy = horizontal ? bounds.height : bounds.width; + int xy1 = Math.max(0, xy / 2 - 12); + int xy2 = Math.min(xy / 2 + 12, xy - 1); + for (int i = divider + 1; i < divider + 4; i += 2) { + if (horizontal) { g.drawLine(i, xy1, i, xy2); } + else { g.drawLine(xy1, i, xy2, i); } + } + Object comp1 = get(component, "component"); + if (comp1 != null) { + paint(g, clipx, clipy, clipwidth, clipheight, comp1, enabled); + Object comp2 = get(comp1, ":next"); + if (comp2 != null) { + paint(g, clipx, clipy, clipwidth, clipheight, comp2, enabled); + } + } + } + else if (("list" == classname) || + ("table" == classname) || ("tree" == classname)) { + Rectangle view = getRectangle(component, ":view"); + Rectangle viewport = getRectangle(component, ":port"); + int[] columnwidths = null; + int lx = clipx - viewport.x - 1; + int xfrom = view.x + Math.max(0, lx); + int xto = view.x + Math.min(viewport.width - 2, lx + clipwidth); + if ("table" == classname) { + columnwidths = new int[getItemCountImpl(component, "column")]; + int i = 0; int x = 0; boolean drawheader = (clipy < viewport.y); + if (drawheader) { g.setClip(viewport.x, 0, viewport.width, viewport.y); } + for (Object column = get(component, "column"); + column != null; column = get(column, ":next")) { + boolean lastcolumn = (i == columnwidths.length - 1); + int width = getInteger(column, "width", 80); + if (lastcolumn) { width = Math.max(width, viewport.width - x); } + columnwidths[i] = width; + if (drawheader && (xfrom < x + width) && (xto > x)) { + paintRect(g, x - view.x, 0, width, viewport.y, + enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, + true, true, false, lastcolumn); + paintContent(column, g, clipx, clipy, clipwidth, clipheight, + x + 2 - view.x, 1, width - 2, + viewport.y - 2, enabled ? c_text : c_disable, "left", false); + } + i++; x += width; + } + if (drawheader) { g.setClip(clipx, clipy, clipwidth, clipheight); } + } + paintRect(g, viewport.x, viewport.y, viewport.width, viewport.height, + enabled ? c_border : c_disable, c_textbg, true, true, true, true); + if (paintScrollPane(g, clipx, clipy, clipwidth, clipheight, bounds, + view, viewport, enabled, inside, pressed)) { + Object lead = get(component, "lead"); + int ly = clipy - viewport.y - 1; + int yfrom = view.y + Math.max(0, ly); + int yto = view.y + Math.min(viewport.height - 2, ly + clipheight); + for (Object item = get(component, ("list" == classname) ? "item" : + (("table" == classname) ? "row" : "node")); item != null;) { + Rectangle r = getRectangle(item, "bounds"); + if (lead == null) { + set(component, "lead", lead = item); // draw first item focused when lead is null + } + if (yto <= r.y) { break; } // the clip bounds are above + + Object next = ("tree" == classname) ? get(item, "node") : null; + boolean expanded = (next != null) && + getBoolean(item, "expanded", true); + if (yfrom < r.y + r.height) { // the clip rectangle is not bellow the current item + boolean selected = getBoolean(item, "selected", false); + paintRect(g, 0, r.y, view.width, r.height, + c_bg, selected ? c_select : c_textbg, false, false, true, false); + if (focus && (lead == item)) { + g.setColor(c_focus); + g.drawRect(0, r.y, view.width - 1, r.height - 2); + } + if ("table" == classname) { + int x = 0; int i = 0; + for (Object cell = get(item, "cell"); + cell != null; cell = get(cell, ":next")) { + if (xto <= x) { break; } + int iwidth = (i < columnwidths.length) ? columnwidths[i] : 80; + if (xfrom < x + iwidth) { + boolean cellenabled = enabled && getBoolean(cell, "enabled", true); + paintContent(cell, g, xfrom, yfrom, xto - xfrom, yto - yfrom, + r.x + x + 1, r.y + 1, iwidth - 2, r.height - 3, + cellenabled ? c_text : c_disable, "left", false); + } + x += iwidth; i++; + } + } else { + boolean itemenabled = enabled && getBoolean(item, "enabled", true); + paintContent(item, g, xfrom, yfrom, xto - xfrom, yto - yfrom, + r.x + 1, r.y + 1, view.width - r.x - 2, + r.height - 3, itemenabled ? c_text : c_disable, "left", false); + + if (next != null) { + int x = r.x - block / 2; + int y = r.y + (r.height - 1) / 2; + //g.drawRect(x - 4, y - 4, 8, 8); + paintRect(g, x - 4, y - 4, 9, 9, itemenabled ? c_border : c_disable, + itemenabled ? c_ctrl : c_bg, true, true, true, true); + g.setColor(itemenabled ? c_text : c_disable); + g.drawLine(x - 2, y, x + 2, y); + if (!expanded) { + g.drawLine(x, y - 2, x, y + 2); + } + } + } + } + if ("tree" == classname) { + if ((next == null) || !expanded) { + while ((item != component) && ((next = get(item, ":next")) == null)) { + item = getParent(item); + } + } + item = next; + } else { + item = get(item, ":next"); + } + } + /*if (columnwidths != null) { + g.setColor(c_bg); + for (int i = 0, cx = -1; i < columnwidths.length - 1; i++) { + cx += columnwidths[i]; + g.drawLine(cx, 0, cx, view.height); + } + }*/ + resetScrollPane(g, clipx, clipy, clipwidth, clipheight, view, viewport); + } + } + else if ("separator" == classname) { + g.setColor(enabled ? c_border : c_disable); + g.fillRect(0, 0, bounds.width, bounds.height); + } + else if ("menubar" == classname) { + Object selected = get(component, "selected"); + int lastx = 0; + for (Object menu = get(component, "menu"); + menu != null; menu = get(menu, ":next")) { + Rectangle mb = getRectangle(menu, "bounds"); + if (clipx + clipwidth <= mb.x) { break; } + if (clipx >= mb.x + mb.width) { continue; } + boolean armed = (selected == menu); + boolean hoover = (selected == null) && (insidepart == menu); + paintRect(g, mb.x, 0, mb.width, bounds.height, enabled ? c_border : c_disable, + enabled ? (armed ? c_select : (hoover ? c_hover : c_ctrl)) : c_bg, + armed, armed, true, armed); + paintContent(menu, g, clipx, clipy, clipwidth, clipheight, + mb.x + 4, 1, mb.width, bounds.height, + (enabled && getBoolean(menu, "enabled", true)) ? c_text : c_disable, + "left", true); + lastx = mb.x + mb.width; + } + paintRect(g, lastx, 0, bounds.width - lastx, bounds.height, + enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, + false, false, true, false); + } + else if ("popupmenu" == classname) { + paintRect(g, 0, 0, bounds.width, bounds.height, + c_border, c_bg, true, true, true, true); + Object selected = get(component, "selected"); + for (Object menu = get(get(component, "menu"), "menu"); + menu != null; menu = get(menu, ":next")) { + Rectangle r = getRectangle(menu, "bounds"); + if (clipy + clipheight <= r.y) { break; } + if (clipy >= r.y + r.height) { continue; } + String itemclass = getClass(menu); + if (itemclass == "separator") { + g.setColor(c_border); + g.fillRect(r.x, r.y, bounds.width - 2, r.height); + } else { + boolean armed = (selected == menu); + boolean menuenabled = getBoolean(menu, "enabled", true); + paintRect(g, r.x, r.y, bounds.width - 2, r.height, c_border, + armed ? c_select : c_bg, false, false, false, false); + int tx = r.x; + if (itemclass == "checkboxmenuitem") { + tx += block + 3; + boolean checked = getBoolean(menu, "selected", false); + String group = getString(menu, "group", null); + g.translate(r.x + 4, r.y + 2); + g.setColor(menuenabled ? c_border : c_disable); + if (group == null) { + g.drawRect(1, 1, block - 3, block - 3); + } else { + g.drawOval(1, 1, block - 3, block - 3); //java + } + if (checked) { + g.setColor(menuenabled ? c_text : c_disable); + if (group == null) { + g.fillRect(3, block - 9, 2, 6); + g.drawLine(3, block - 4, block - 4, 3); + g.drawLine(4, block - 4, block - 4, 4); + } else { + g.fillOval(5, 5, block - 10, block - 10); //java + g.drawOval(4, 4, block - 9, block - 9); //java + } + } + g.translate(-r.x - 4, -r.y - 2); + } + paintContent(menu, g, clipx, clipy, clipwidth, clipheight, + tx + 4, r.y + 2, bounds.width - 10, + r.height - 4, menuenabled ? c_text : c_disable, "left", true); + if (itemclass == "menu") { + paintArrow(g, r.x + bounds.width - block, r.y, block, r.height, 'E'); + } + } + } + } + //java> + else if ("bean" == classname) { + g.clipRect(0, 0, bounds.width, bounds.height); + ((Component) get(component, "bean")).paint(g); + g.setClip(clipx, clipy, clipwidth, clipheight); + } + //translate(-bounds.x, -bounds.y); + clipx += bounds.x; clipy += bounds.y; + } + + /** + * + */ + private void paintReverse(Graphics g, + int clipx, int clipy, int clipwidth, int clipheight, + Object component, boolean enabled) { + if (component != null) { + Rectangle bounds = getRectangle(component, "bounds"); + if ((clipx < bounds.x) || + (clipx + clipwidth > bounds.x + bounds.width) || + (clipy < bounds.y) || + (clipy + clipheight > bounds.y + bounds.height)) { + paintReverse(g, clipx, clipy, clipwidth, clipheight, + get(component, ":next"), enabled); + } + paint(g, clipx, clipy, clipwidth, clipheight, component, enabled); + } + } + + /** + * + */ + private void paintField(Graphics g, + int clipx, int clipy, int clipwidth, int clipheight, Object component, + int width, int height, boolean inside, boolean pressed, + boolean focus, boolean enabled, boolean hidden, int left) { + boolean editable = getBoolean(component, "editable", true); + paintRect(g, 0, 0, width, height, enabled ? c_border : c_disable, + editable ? c_textbg : c_bg, true, true, true, true); + g.clipRect(1 + left, 1, width - left - 2, height - 2); + + String text = getString(component, "text", ""); + int offset = getInteger(component, "offset", 0); + FontMetrics fm = g.getFontMetrics(); //java + + int caret = 0; + if (focus) { + int start = getInteger(component, "start", 0); + int end = getInteger(component, "end", 0); + caret = hidden ? (fm.charWidth('*') * end) : + fm.stringWidth(text.substring(0, end)); //java + //midp font.substringWidth(text, 0, end); + if (start != end) { + int is = hidden ? (fm.charWidth('*') * start) : + fm.stringWidth(text.substring(0, start)); //java + //midp font.substringWidth(text, 0, start); + g.setColor(c_select); + g.fillRect(2 + left - offset + Math.min(is, caret), 1, + Math.abs(caret - is), height - 2); + } + } + + if (focus) { + g.setColor(c_focus); + g.fillRect(1 + left - offset + caret, 1, 1, height - 2); + } + + g.setColor(enabled ? c_text : c_disable); + int fx = 2 + left - offset; + int fy = (height + fm.getAscent() - fm.getDescent()) / 2; //java + //midp int fy = (height - font.getHeight()) / 2; + if (hidden) { + int fh = fm.charWidth('*'); + for (int i = text.length(); i > 0; i--) { + g.drawString("*", fx, fy); //java + //midp g.drawChar('*', fx, fy, Graphics.LEFT | Graphics.TOP); + fx += fh; + } + } else { + g.drawString(text, fx, fy); //java + //midp g.drawString(text, fx, fy, Graphics.LEFT | Graphics.TOP); + } + g.setClip(clipx, clipy, clipwidth, clipheight); + } + + /** + * + */ + private boolean paintScrollPane(Graphics g, + int clipx, int clipy, int clipwidth, int clipheight, Rectangle bounds, + Rectangle view, Rectangle viewport, + boolean enabled, boolean inside, boolean pressed) { + if ((viewport.y + viewport.height < bounds.height) && + (clipy + clipheight > viewport.y + viewport.height)) { // need horizontal + int x = viewport.x; + int y = viewport.y + viewport.height; + int height = bounds.height - y; + int button = Math.min(block, viewport.width / 2); + int track = viewport.width - (2 * button); //max 10 + int knob = Math.min(track, Math.max(track * (viewport.width - 2) / view.width, 6)); + int decrease = view.x * (track - knob) / + (view.width - viewport.width + 2); + int increase = track - decrease - knob; + paintArrow(g, x, y, button, height, + 'W', enabled, inside, pressed, "left", false, true, true, false); + paintRect(g, x + button, y, decrease, height, + enabled ? c_border : c_disable, c_bg, false, true, true, false); + paintRect(g, x + button + decrease, y, knob, height, + enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, false, true, true, true); + int n = Math.min(5, (knob - 4) / 3); + g.setColor(enabled ? c_border : c_disable); + int cx = (x + button + decrease) + (knob + 2 - n * 3) / 2; + for (int i = 0; i < n; i++ ) { + g.drawLine(cx + i * 3, y + 2, cx + i * 3, y + height - 4); + } + paintRect(g, x + button + decrease + knob, y, increase, height, + enabled ? c_border : c_disable, c_bg, false, false, true, true); + paintArrow(g, x + button + track, y, button, height, + 'E', enabled, inside, pressed, "right", false, false, true, true); + } + if ((viewport.x + viewport.width < bounds.width) && + (clipx + clipwidth > viewport.x + viewport.width)) { // need vertical + int x = viewport.x + viewport.width; + int y = viewport.y; + int width = bounds.width - x; + //if (y > 0) { + // paintRect(g, x - 1, 0, width + 1, y, + // enabled ? c_border : c_disable, c_bg, false, true, false, false); + //} + int track = viewport.height - (2 * block); + int knob = track * (viewport.height - 2) / view.height; + int decrease = view.y * (track - knob) / + (view.height - viewport.height + 2); + int increase = track - decrease - knob; + paintArrow(g, x, y, width, block, + 'N', enabled, inside, pressed, "up", true, false, false, true); + paintRect(g, x, y + block, width, decrease, + enabled ? c_border : c_disable, c_bg, true, false, false, true); + paintRect(g, x, y + block + decrease, width, knob, + enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, true, false, true, true); + int n = Math.min(5, (knob - 4) / 3); + g.setColor(enabled ? c_border : c_disable); + int cy = (y + block + decrease) + (knob + 2 - n * 3) / 2; + for (int i = 0; i < n; i++ ) { + g.drawLine(x + 2, cy + i * 3, x + width - 4, cy + i * 3); + } + paintRect(g, x, y + block + decrease + knob, width, increase, + enabled ? c_border : c_disable, c_bg, false, false, true, true); + paintArrow(g, x, y + block + track, width, block, + 'S', enabled, inside, pressed, "down", false, false, true, true); + } + if ((clipx + clipwidth > viewport.x) && (clipy + clipheight > viewport.y) && + (clipx < viewport.x + viewport.width) && (clipy < viewport.y + viewport.height)) { + g.clipRect(viewport.x + 1, viewport.y + 1, viewport.width - 2, viewport.height - 2); + g.translate(viewport.x + 1 - view.x, viewport.y + 1 - view.y); + return true; + } + return false; + } + + /** + * + */ + private void resetScrollPane(Graphics g, + int clipx, int clipy, int clipwidth, int clipheight, + Rectangle view, Rectangle viewport) { + g.translate(view.x - viewport.x - 1, view.y - viewport.y - 1); + g.setClip(clipx, clipy, clipwidth, clipheight); + } + + /** + * + */ + private void paintRect(Graphics g, int x, int y, int width, int height, + Color border, Color bg, + boolean top, boolean left, boolean bottom, boolean right) { + if ((width <= 0) || (height <= 0)) return; + g.setColor(border); + if (top) { + g.drawLine(x + width - 1, y, x, y); + y++; height--; if (height <= 0) return; + } + if (left) { + g.drawLine(x, y, x, y + height - 1); + x++; width--; if (width <= 0) return; + } + if (bottom) { + g.drawLine(x, y + height - 1, x + width - 1, y + height - 1); + height--; if (height <= 0) return; + } + if (right) { + g.drawLine(x + width - 1, y + height - 1, x + width - 1, y); + width--; if (width <= 0) return; + } + + //java> + if (bg == c_ctrl) { + if (height > block) { + g.setColor(c_bg); + g.fillRect(x, y, width, height - block); + } + for (int i = 0; i < width; i += block) { + g.drawImage(gradient, x + i, (height > block) ? (y + height - block) : y, + x + Math.min(i + block, width), y + height, + 0, 0, Math.min(block, width - i), Math.min(block, height), null); + } + /*if (width > block) { + g.setColor(c_bg); + g.fillRect(x, y, width - block, height); + } + for (int i = 0; i < height; i += block) { + g.drawImage(gradient, (width > block) ? (x + width - block) : x, y + i, + x + width, y + Math.min(i + block, height), + 0, 0, Math.min(block, width), Math.min(block, height - i), null); + }*/ + } + else { + g.setColor(bg); + g.fillRect(x, y, width, height); + } + //Graphics g, int x, int y, int width, int height, + char dir, boolean enabled, boolean inside, boolean pressed, String part, + boolean top, boolean left, boolean bottom, boolean right) { + inside = inside && (insidepart == part); + pressed = pressed && (pressedpart == part); + paintRect(g, x, y, width, height, enabled ? c_border : c_disable, + enabled ? ((inside != pressed) ? c_hover : + (pressed ? c_press : c_ctrl)) : c_bg, + top, left, bottom, right); + g.setColor(enabled ? c_text : c_disable); + paintArrow(g, x, y, width, height, dir); + } + + /** + * + */ + private void paintArrow(Graphics g, + int x, int y, int width, int height, char dir) { + int cx = x + width / 2 - 2; + int cy = y + height / 2 - 2; + for (int i = 0; i < 4; i++) { + if (dir == 'N') { // north + g.drawLine(cx + 1 - i, cy + i, cx + 1/*2*/ + i, cy + i); + } + else if (dir == 'W') { // west + g.drawLine(cx + i, cy + 1 - i, cx + i, cy + 1/*2*/ + i); + } + else if (dir == 'S') { // south + g.drawLine(cx + 1 - i, cy + 4 - i, cx + 1/*2*/ + i, cy + 4 - i); + } + else { // east + g.drawLine(cx + 4 - i, cy + 1 - i, cx + 4 - i, cy + 1/*2*/ + i); + } + } + } + + /** + * + */ + private void paintContent(Object component, Graphics g, + int clipx, int clipy, int clipwidth, int clipheight, + int x, int y, int width, int height, Color fg, String defaultalignment, + boolean checkmnemonic) { + String text = getString(component, "text", null); + Image icon = getIcon(component, "icon", null); + if ((text == null) && (icon == null)) { return; } + String alignment = getString(component, "alignment", defaultalignment); + + FontMetrics fm = null; //java + int tw = 0, th = 0; + int ta = 0; //java + if (text != null) { + fm = g.getFontMetrics(); //java + tw = fm.stringWidth(text); + ta = fm.getAscent(); //java + th = fm.getDescent() + ta; //java + //midp th = font.getHeight(); + g.setColor(fg); + } + int iw = 0, ih = 0; + if (icon != null) { + iw = icon.getWidth(this); + ih = icon.getHeight(this); //java + } + + boolean clipped = (tw + iw > width) || (th > height) || (ih > height); + int cx = x; + if ("center" == alignment) { cx += (width - tw - iw) / 2; } + else if ("right" == alignment) { cx += width - tw - iw; } + + if (clipped) { g.clipRect(x, y, width, height); } + if (icon != null) { + g.drawImage(icon, cx, y + (height - ih) / 2, this); //java + //midp g.drawImage(icon, cx, y + height / 2, Graphics.LEFT | Graphics.VCENTER); + cx += iw; + } + if (text != null) { + int ty = y + (height - th) / 2 + ta; //java + g.drawString(text, cx, ty); //java + //midp g.drawString(text, cx, y + (height - th) / 2, Graphics.LEFT | Graphics.TOP); + if (checkmnemonic) { + int mnemonic = getInteger(component, "mnemonic", -1); + if ((mnemonic != -1) && (mnemonic < text.length())) { + int mx = cx + fm.stringWidth(text.substring(0, mnemonic)); //java + //midp int mx = cx + font.substringWidth(text, 0, mnemonic); + //midp int ty = (height + th) / 2; + g.drawLine(mx, ty + 1, mx + fm.charWidth(text.charAt(mnemonic)), ty + 1); + } + } + } + if (clipped) { g.setClip(clipx, clipy, clipwidth, clipheight); } + } + //midp private void setTimer(long delay) {} + //java> + + /** + * + */ + public synchronized void run() { + while (timer == Thread.currentThread()) { + try { + if (watch == 0) { + wait(0); + } else { + long current = System.currentTimeMillis(); + if (watch > current) { + wait(watch - current); + } else { + watch = 0; + if ((watchdelay == 300L) || (watchdelay == 60L)) { + if (processScroll(mousepressed, pressedpart)) { setTimer(60L); } + } else if ((watchdelay == 375L) || (watchdelay == 75L)) { + if (processSpin(mousepressed, pressedpart)) { setTimer(75L); } + } else if (watchdelay == 750L) { + //System.out.println("> tip: " + getClass(mouseinside) + " : " + ((insidepart instanceof Object[]) ? getClass(insidepart) : insidepart)); + showTip(); + } + } + } + } catch (InterruptedException ie) {} //ie.printStackTrace(); + } + } + + /** + * + */ + private void setTimer(long delay) { + watchdelay = delay; + if (delay == 0) { + watch = 0; + } else { + long prev = watch; + watch = System.currentTimeMillis() + delay; + if (timer == null) { + timer = new Thread(this); + timer.setPriority(Thread.MIN_PRIORITY); + timer.setDaemon(true); + timer.start(); + } + if ((prev == 0) || (watch < prev)) { + synchronized (this) { notify(); } //try {}catch (IllegalMonitorStateException imse) {} + } + } + } + + /** + * + */ + public boolean isFocusTraversable() { + return true; + } + + // 0) { + processKeyPress((popupowner != null) ? popupowner : focusowner, + false, false, 1, keyCode, 0); + } + else { + int keychar = 0, key = 0; + switch (getGameAction(keyCode)) { + case UP: key = KeyEvent.VK_UP; break; + case LEFT: key = KeyEvent.VK_LEFT; break; + case DOWN: key = KeyEvent.VK_DOWN; break; + case RIGHT: key = KeyEvent.VK_RIGHT; break; + case FIRE: key = KeyEvent.VK_ENTER; keychar = KeyEvent.VK_SPACE; break; + case GAME_A: key = KeyEvent.VK_ESCAPE; break; + } + if (key != 0) { + processKeyPress((popupowner != null) ? popupowner : focusowner, + false, false, 1, keychar, key); + } + //if (keyCode == getKeyCode(LEFT)) { + } + } + } + + protected void keyRepeated(int keyCode) { + keyPressed(keyCode); + } + + private static final Command nextcommand = new Command("Next", Command.SCREEN, 0); + //private static final Command prevcommand = new Command("Previous", Command.SCREEN, 0); + { + addCommand(nextcommand); + //addCommand(prevcommand); + setCommandListener(this); + } + + public void commandAction(Command command, Displayable displayable) { + if (command == nextcommand) { + setNextFocusable(focusowner, false); + repaint(focusowner); + closeup(); + } + //else if (command == prevcommand) { + //setPreviousFocusable(focusowner, null, true, true, false); + //repaint(focusowner); + //closeup(); + //} + } + midp*/ + //java> + + /** + * + */ + protected void processEvent(AWTEvent e) { + int id = e.getID(); + if ((id >= MouseEvent.MOUSE_FIRST) && (id <= MouseEvent.MOUSE_LAST)) { + MouseEvent me = (MouseEvent) e; + int x = me.getX(); + int y = me.getY(); + int clickcount = me.getClickCount(); + boolean shiftdown = me.isShiftDown(); + boolean controldown = me.isControlDown(); + boolean popuptrigger = me.isPopupTrigger(); + if (id == MouseEvent.MOUSE_ENTERED) { + if (mousepressed == null) { + findComponent(content, x, y); + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_ENTERED, mouseinside, insidepart); + } + } + else if (id == MouseEvent.MOUSE_MOVED) { + Object previnside = mouseinside; + Object prevpart = insidepart; + findComponent(content, x, y); + if ((previnside == mouseinside) && (prevpart == insidepart)) { + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_MOVED, mouseinside, insidepart); + } + else { + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_EXITED, previnside, prevpart); + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_ENTERED, mouseinside, insidepart); + } + } + else if (id == MouseEvent.MOUSE_EXITED) { + if (mousepressed == null) { + Object mouseexit = mouseinside; + Object exitpart = insidepart; + mouseinside = insidepart = null; + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_EXITED, mouseexit, exitpart); + } + } + else if (id == MouseEvent.MOUSE_PRESSED) { + if (popupowner != null) { + String classname = getClass(mouseinside); + if ((popupowner != mouseinside) && + (classname != "popupmenu") && (classname != "combolist")) { + closeup(); + } + } + mousepressed = mouseinside; + pressedpart = insidepart; + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_PRESSED, mousepressed, pressedpart); + } + else if (id == MouseEvent.MOUSE_DRAGGED) { + Object previnside = mouseinside; + Object prevpart = insidepart; + findComponent(content, x, y); + boolean same = (previnside == mouseinside) && (prevpart == insidepart); + boolean isin = (mousepressed == mouseinside) && (pressedpart == insidepart); + boolean wasin = (mousepressed == previnside) && (pressedpart == prevpart); + + if (wasin && !isin) { + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_EXITED, mousepressed, pressedpart); + } + else if (!same && (popupowner != null) && !wasin) { + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + DRAG_EXITED, previnside, prevpart); + } + if (isin && !wasin) { + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_ENTERED, mousepressed, pressedpart); + } + else if (!same && (popupowner != null) && !isin) { + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + DRAG_ENTERED, mouseinside, insidepart); + } + if (isin == wasin) { + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_DRAGGED, mousepressed, pressedpart); + } + } + else if (id == MouseEvent.MOUSE_RELEASED) { + Object mouserelease = mousepressed; + Object releasepart = pressedpart; + mousepressed = pressedpart = null; + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_RELEASED, mouserelease, releasepart); + if ((mouseinside != null) && + ((mouserelease != mouseinside) || (releasepart != insidepart))) { + handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger, + MouseEvent.MOUSE_ENTERED, mouseinside, insidepart); + } + } + } + else if (id == MOUSE_WHEEL) { + Rectangle port = getRectangle(mouseinside, ":port"); + if (port != null) { // is scrollable + Rectangle bounds = getRectangle(mouseinside, "bounds"); + try { // mouse wheel is supported since 1.4 thus it use reflection + if (wheelrotation == null) { + wheelrotation = e.getClass().getMethod("getWheelRotation", null); + } + int rotation = ((Integer) wheelrotation.invoke(e, null)).intValue(); + + if (port.x + port.width < bounds.width) { // has vertical scrollbar + processScroll(mouseinside, (rotation > 0) ? "down" : "up"); + } + else if (port.y + port.height < bounds.height) { // has horizontal scrollbar + processScroll(mouseinside, (rotation > 0) ? "right" : "left"); + } + } catch (Exception exc) { /* never */ } + } + } + else if ((id == KeyEvent.KEY_PRESSED) || (id == KeyEvent.KEY_TYPED)) { + if (focusinside && ((popupowner != null) || (focusowner != null))) { + hideTip(); + KeyEvent ke = (KeyEvent) e; + int keychar = ke.getKeyChar(); + boolean control = (keychar <= 0x1f) || + ((keychar >= 0x7f) && (keychar <= 0x9f)) || + (keychar >= 0xffff) || ke.isControlDown(); + if (control == (id == KeyEvent.KEY_PRESSED)) { + int keycode = control ? ke.getKeyCode() : 0; + if (!processKeyPress((popupowner != null) ? popupowner : focusowner, + ke.isShiftDown(), ke.isControlDown(), ke.getModifiers(), + control ? 0 : keychar, keycode)) { + if ((keycode == KeyEvent.VK_TAB) || + ((keycode == KeyEvent.VK_F6) && (ke.isAltDown() || ke.isControlDown()))) { + boolean outgo = (keycode == KeyEvent.VK_F6); + if (!ke.isShiftDown() ? setNextFocusable(focusowner, outgo) : + setPreviousFocusable(focusowner, outgo)) { + ke.consume(); + } else if (MOUSE_WHEEL != 0) { // 1.4 + if (!ke.isShiftDown()) { + transferFocus(); + } + else { try { + getClass().getMethod("transferFocusBackward", null). invoke(this, null); + } catch (Exception exc) { /* never */ } } + } + repaint(focusowner); + closeup(); + } + else if (keycode == KeyEvent.VK_F8) { + for (Object splitpane = focusowner; + splitpane != null; splitpane = getParent(splitpane)) { + if (getClass(splitpane) == "splitpane") { + setFocus(splitpane); repaint(splitpane); break; //middle + } + } + } + } + else ke.consume(); + /*else if (keycode == KeyEvent.VK_F10) { + Object menubar = null; // find("class", "menubar") + if ((menubar != null) && (get(menubar, "selected") == null)) { + set(menubar, "selected", getMenu(menubar, null, true, false)); + Object popup = popup(menubar, "menubar"); + set(popup, "selected", getMenu(popup, null, true, true)); + repaint(menubar); // , selected + } + }*/ + } + } + } + /*else if (id == KeyEvent.KEY_RELEASED) { + if (focusinside && (focusowner != null)) { + KeyEvent ke = (KeyEvent) e; + //pressedkey = 0; + processKeyRelease(focusowner, ke, ke.getKeyCode()); + } + }*/ + else if (id == FocusEvent.FOCUS_LOST) { + focusinside = false; + if (focusowner != null) { repaint(focusowner); } + closeup(); + } + else if (id == FocusEvent.FOCUS_GAINED) { + focusinside = true; + if (focusowner == null) { setFocus(content); } + else { repaint(focusowner); } + } + else if ((id == ComponentEvent.COMPONENT_RESIZED) || + (id == ComponentEvent.COMPONENT_SHOWN)) { + Dimension d = getSize(); + //System.out.println(id + ": " + d.width + ", " + d.height); + setRectangle(content, "bounds", 0, 0, d.width, d.height); + validate(content); + closeup(); + if (!focusinside) { requestFocus(); } + } + } + + /** + * + */ + /*private boolean processKeyPress(Object component, + KeyEvent e, int keycode, Object invoker) { + if (processKeyPress(component, e, keycode)) { return true; } + for (Object comp = get(component, "component"); + comp != null; comp = get(comp, ":next")) { + if ((comp != invoker) && processKeyPress(comp, e, keycode, null)) { + return true; + } + } + if ((invoker != null) && (component != content)) { + Object parent = getParent(component); + if (parent != null) { + return processKeyPress(parent, e, keycode, component); + } + } + return false; + }*/ + //KeyEvent.VK_SPACE || + ((keycode == KeyEvent.VK_ENTER) && + (getString(component, "type", null) == "default")) || + ((keycode == KeyEvent.VK_ESCAPE) && //... + (getString(component, "type", null) == "cancel"))) { + //pressedkey = keychar; + invoke(component, "action"); + repaint(component); + return true; + } + } + else if ("checkbox" == classname) { + if (keychar == KeyEvent.VK_SPACE) { + changeCheck(component, true); + repaint(component); + return true; + } + } + else if ("combobox" == classname) { + Object combolist = get(component, "combolist"); + if (combolist == null) { + if (getBoolean(component, "editable", true) && + processField(component, shiftdown, controldown, modifiers, + keychar, keycode, false, false)) { + setInteger(component, "selected", -1, -1); + return true; + } + if ((keychar == KeyEvent.VK_SPACE) || (keycode == KeyEvent.VK_DOWN)) { + combolist = popup(component, classname); + int selected = getInteger(component, "selected", -1); + set(combolist, "inside", (selected != -1) ? + getItemImpl(component, "choice", selected) : + get(component, "choice")); //scroll to it! + } + else return false; + } else { + if ((keycode == KeyEvent.VK_UP) || + (keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_UP) || + (keycode == KeyEvent.VK_PAGE_DOWN) || + (keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_END)) { + Object selected = get(combolist, "inside"); + Object next = getListItem(component, combolist, + keycode, selected, "choice", null); + if (next != null) { + set(combolist, "inside", next); + Rectangle r = getRectangle(next, "bounds"); + scrollToVisible(combolist, r.x, r.y, 0, r.height); + if (selected != null) { repaint(combolist, "combolist", selected); } + repaint(combolist, "combolist", next); + } + } + else if ((keycode == KeyEvent.VK_ENTER) || (keychar == KeyEvent.VK_SPACE)) { + closeup(component, combolist, get(combolist, "inside")); //Alt+Up + } + else if (keycode == KeyEvent.VK_ESCAPE) { + closeup(component, combolist, null); + } + else return processField(component, shiftdown, controldown, modifiers, + keychar, keycode, false, false); + } + return true; + } + else if (("textfield" == classname) || ("passwordfield" == classname)) { + return processField(component, shiftdown, controldown, modifiers, + keychar, keycode, false, ("passwordfield" == classname)); + } + else if ("textarea" == classname) { + String text = getString(component, "text", ""); + int start = getInteger(component, "start", 0); + int end = getInteger(component, "end", 0); + + int istart = start; + int iend = end; + String insert = null; + if ((keycode == KeyEvent.VK_HOME) && !controldown) { + while ((iend > 0) && (text.charAt(iend - 1) != '\n')) { iend--; } + if (!shiftdown) { istart = iend; } + } + else if ((keycode == KeyEvent.VK_END) && !controldown) { + iend = text.indexOf('\n', end); + if (iend == -1) { iend = text.length(); } + if (!shiftdown) { istart = iend; } + } + else if ((keycode == KeyEvent.VK_UP) || + (keycode == KeyEvent.VK_PAGE_UP)) { + int prev = end; + while ((prev > 0) && (text.charAt(prev - 1) != '\n')) { prev--; } + if (prev != 0) { + int dx = end - prev; + int lines = (keycode == KeyEvent.VK_PAGE_UP) ? + (getRectangle(component, ":port").height / + getFontMetrics(getFont()).getHeight()) : 1; + int first = prev; + do { + prev = first; first--; lines--; + while ((first > 0) && (text.charAt(first - 1) != '\n')) { first--; } + } while ((first > 0) && (lines > 0)); + iend = Math.min(first + dx, prev - 1); + if (!shiftdown) { istart = iend; } + } + } + else if ((keycode == KeyEvent.VK_DOWN) || + (keycode == KeyEvent.VK_PAGE_DOWN)) { + int next = text.indexOf('\n', end); + if (next != -1) { + int prev = end; + while ((prev > 0) && (text.charAt(prev - 1) != '\n')) { prev--; } + if (keycode == KeyEvent.VK_PAGE_DOWN) { + int lines = getRectangle(component, ":port").height / + getFontMetrics(getFont()).getHeight(); + for (int more = 0; (lines > 1) && + ((more = text.indexOf('\n', next + 1)) != -1); next = more) { + lines--; + } + } + int last = text.indexOf('\n', next + 1); + iend = Math.min(next + 1 + end - prev, + (last == -1) ? (text.length() + 1) : last); + if (!shiftdown) { istart = iend; } + } + } + return changeField(component, text, insert, istart, iend, start, end) ? + true : processField(component, shiftdown, controldown, modifiers, + keychar, keycode, true, false); + } + else if ("tabbedpane" == classname) { + if ((keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN) || + (keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) { + int selected = getInteger(component, "selected", 0); + boolean increase = (keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN); + int newvalue = selected; + int n = increase ? getItemCountImpl(component, "tab") : 0; + int d = (increase ? 1 : -1); + for (int i = selected + d; increase ? (i < n) : (i >= 0); i += d) { + if (getBoolean(getItemImpl(component, "tab", i), "enabled", true)) { + newvalue = i; break; + } + } + if (newvalue != selected) { + setInteger(component, "selected", newvalue, 0); + repaint(component); + invoke(component, "action"); + } + } + } + else if ("spinbox" == classname) { + if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_DOWN)) { + processSpin(component, (keycode == KeyEvent.VK_UP)? "up" : "down"); + return true; + } + return processField(component, shiftdown, controldown, modifiers, + keychar, keycode, false, false); + } + else if ("slider" == classname) { + int value = getInteger(component, "value", 0); + int d = 0; + if ((keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_LEFT) || + (keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_PAGE_UP)) { + d = getInteger(component, "minimum", 0) - value; + if ((keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) { + d = Math.max(d, -getInteger(component, "unit", 5)); + } + else if (keycode == KeyEvent.VK_PAGE_UP) { + d = Math.max(d, -getInteger(component, "block", 25)); + } + } + else if ((keycode == KeyEvent.VK_END) || (keycode == KeyEvent.VK_RIGHT) || + (keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_DOWN)) { + d = getInteger(component, "maximum", 100) - value; + if ((keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN)) { + d = Math.min(d, getInteger(component, "unit", 5)); + } + else if (keycode == KeyEvent.VK_PAGE_DOWN) { + d = Math.min(d, getInteger(component, "block", 25)); + } + } + if (d != 0) { + setInteger(component, "value", value + d, 0); + repaint(component); + invoke(component, "action"); + } + } + else if ("splitpane" == classname) { + int divider = getInteger(component, "divider", -1); + int d = 0; + if (keycode == KeyEvent.VK_HOME) { + d = -divider; + } + else if ((keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) { + d = Math.max(-10, -divider); + } + else if ((keycode == KeyEvent.VK_END) || + (keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN)) { + boolean horizontal = ("vertical" != get(component, "orientation")); + Rectangle bounds = getRectangle(component, "bounds"); + int max = (horizontal ? bounds.width : bounds.height) - 5; + d = max - divider; + if (keycode != KeyEvent.VK_END) { + d = Math.min(d, 10); + } + } + if (d != 0) { + setInteger(component, "divider", divider + d, -1); + validate(component); + } + } + else if ("list" == classname) { + return processList(component, shiftdown, controldown, keychar, keycode, "item", null); + } + else if ("table" == classname) { + return processList(component, shiftdown, controldown, keychar, keycode, "row", null); + } + else if ("tree" == classname) { + //? clear childs' selection, select this is its subnode was selected + if (keycode == KeyEvent.VK_LEFT) { + Object lead = get(component, "lead"); + if ((get(lead, "node") != null) && getBoolean(lead, "expanded", true)) { + setBoolean(lead, "expanded", false, true); + selectItem(component, lead, "node", "node"); + validate(component); + invoke(component, "collapse"); //lead + return true; + } + else { + Object parent = getParent(lead); + if (parent != component) { + selectItem(component, parent, "node", "node"); + setLead(component, lead, parent); + return true; + } + } + } + //? for interval mode select its all subnode or deselect all after + else if (keycode == KeyEvent.VK_RIGHT) { + Object lead = get(component, "lead"); + Object node = get(lead, "node"); + if (node != null) { + if (getBoolean(lead, "expanded", true)) { + selectItem(component, node, "node", "node"); + setLead(component, lead, node); + } + else { + setBoolean(lead, "expanded", true, true); + selectItem(component, lead, "node", "node"); + validate(component); + invoke(component, "expand"); //lead + } + return true; + } + } + return processList(component, shiftdown, controldown, keychar, keycode, "node", "node"); + } + else if ("menubar" == classname) { + Object previous = null; Object last = null; + for (Object i = get(component, "popupmenu"); + i != null; i = get(i, "popupmenu")) { + previous = last; last = i; + } + Object selected = get(last, "selected"); + Object hotpopup = ((selected != null) || (previous == null)) ? + last : previous; + if ((selected == null) && (previous != null)) { + selected = get(previous, "selected"); + } + + if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_DOWN)) { + set(hotpopup, "selected", null); + popup(hotpopup, "popupmenu"); + selected = getMenu(hotpopup, + selected, keycode == KeyEvent.VK_DOWN, true); + set(hotpopup, "selected", selected); + repaint(hotpopup); + } + else if (keycode == KeyEvent.VK_LEFT) { + if (previous != null) { + selected = get(previous, "selected"); + set(previous, "selected", null); + popup(previous, "popupmenu"); + set(previous, "selected", selected); + repaint(previous); // , selected + } + else { + selected = getMenu(component, get(component, "selected"), false, false); + set(component, "selected", selected); + Object popup = popup(component, "menubar"); + set(popup, "selected", getMenu(popup, null, true, true)); + repaint(component); // , selected + } + } + else if (keycode == KeyEvent.VK_RIGHT) { + if ((previous != null) && (selected == null)) { + set(last, "selected", get(get(last, "menu"), "menu")); + repaint(last); // , selected + } + else if ((selected != null) && (getClass(selected) == "menu")) { + Object popup = popup(last, "popupmenu"); + set(popup, "selected", get(get(popup, "menu"), "menu")); + } + else { + selected = getMenu(component, get(component, "selected"), true, false); + set(component, "selected", selected); + Object popup = popup(component, "menubar"); + set(popup, "selected", getMenu(popup, null, true, true)); + repaint(component); // , selected + } + } + else if ((keycode == KeyEvent.VK_ENTER) || + (keychar == KeyEvent.VK_SPACE) || (keycode == KeyEvent.VK_ESCAPE)) { + if ((keycode != KeyEvent.VK_ESCAPE) && + getBoolean(selected, "enabled", true)) { + if ((selected != null) && (getClass(selected) == "checkboxmenuitem")) { + changeCheck(selected, false); + } + else invoke(selected, "action"); + } + closeup(component); + } + else return false; + return true; + } + return false; + } + + /** + * + */ + private boolean changeCheck(Object component, boolean box) { + String group = getString(component, "group", null); + if (group != null) { + if (getBoolean(component, "selected", false)) { return false; } + for (Object comp = get(getParent(component), + box ? "component" : "menu"); comp != null; comp = get(comp, ":next")) { + if (comp == component) { + setBoolean(component, "selected", true); + } + else if (group.equals(get(comp, "group")) && + getBoolean(comp, "selected", false)) { + setBoolean(comp, "selected", false); + if (box) { repaint(comp); } //checkbox only + } + } + } + else { + setBoolean(component, "selected", + !getBoolean(component, "selected", false), false); + } + invoke(component, "action"); + return true; + } + + /** + * + */ + private Object getMenu(Object component, Object part, + boolean forward, boolean popup) { + if (forward) { + if (part != null) { part = get(part, ":next"); } + if (part == null) { + part = get(popup ? get(component, "menu") : component, "menu"); + } + } + else { + Object prev = get(popup ? get(component, "menu") : component, "menu"); + for (Object next = get(prev, ":next"); + (next != null) && (next != part); next = get(next, ":next")) { + prev = next; + } + part = prev; + } + return (getClass(part) == "separator") ? + getMenu(component, part, forward, popup) : part; + } + + /** + * + */ + /*private boolean processKeyRelease(Object component, KeyEvent e, int keycode) { + return true; + }*/ + + /** + * + */ + private boolean processField(Object component, + boolean shiftdown, boolean controldown, int modifiers, + int keychar, int keycode, boolean multiline, boolean hidden) { + String text = getString(component, "text", ""); + int start = getInteger(component, "start", 0); + int end = getInteger(component, "end", 0); + boolean editable = getBoolean(component, "editable", true); + + int istart = start; + int iend = end; + String insert = null; + //midp if (editable && (keychar != 0)) { + if (editable && (keychar != 0) && //java + ((modifiers == 0) || (modifiers == InputEvent.SHIFT_MASK))) { //java + insert = String.valueOf((char) keychar); + } + else if (multiline && editable && (keycode == KeyEvent.VK_ENTER)) { + insert = "\n"; + } + else if (editable && (keycode == KeyEvent.VK_BACK_SPACE)) { + insert = ""; + if (start == end) { istart -= 1; } + } + else if (keycode == KeyEvent.VK_END) { + iend = text.length(); + if (!shiftdown) { istart = iend; } + } + else if (keycode == KeyEvent.VK_HOME) { + iend = 0; + if (!shiftdown) { istart = iend; } + } + else if (keycode == KeyEvent.VK_LEFT) { + //java> + if (controldown) { + for (int i = 0; i < 2; i++) { + while ((iend > 0) && ((i != 0) == + Character.isLetterOrDigit(text.charAt(iend - 1)))) { iend--; } + } + } else { + iend -= 1; + } + //KeyEvent.VK_RIGHT) { + //java> + if (controldown) { + for (int i = 0; i < 2; i++) { + while ((iend < text.length()) && ((i == 0) == + Character.isLetterOrDigit(text.charAt(iend)))) { iend++; } + } + } else { + iend += 1; + } + //KeyEvent.VK_DELETE)) { + insert = ""; + if (start == end) { iend += 1; } + } + else if (controldown && + ((keycode == KeyEvent.VK_A) || (keycode == 0xBF))) { + istart = 0; // KeyEvent.VK_SLASH + iend = text.length(); + } + else if (controldown && (keycode == 0xDC)) { + istart = iend = text.length(); // KeyEvent.VK_BACK_SLASH + } + else if ((editable && !hidden && controldown && (keycode == KeyEvent.VK_X)) || + (!hidden && controldown && (keycode == KeyEvent.VK_C))) { + if (start != end) { + clipboard = text.substring( + Math.min(start, end), Math.max(start, end)); + //java> + try { + /* Personal Basis Profile doesn't contain datatransfer package + Toolkit toolkit = getToolkit(); + Object systemclipboard = toolkit.getClass().getMethod("getSystemClipboard", null).invoke(toolkit, null); + Class selectionclass = Class.forName("java.awt.datatransfer." + "StringSelection"); + Object selection = selectionclass.getConstructor(new Class[] { String.class }). + newInstance(new Object[] { clipboard }); + systemclipboard.getClass().getMethod("setContents", new Class[] { + Class.forName("java.awt.datatransfer." + "Transferable"), + Class.forName("java.awt.datatransfer." + "ClipboardOwner") }). + invoke(systemclipboard, new Object[] { selection, null });*/ + getToolkit().getSystemClipboard().setContents( + new StringSelection(clipboard), null); + } catch (Exception exc) {} + //KeyEvent.VK_X) { insert = ""; } + } + } + else if (editable && controldown && (keycode == KeyEvent.VK_V)) { + //java> + try { + /* no datatransfer package in PBP + Toolkit toolkit = getToolkit(); + Object systemclipboard = toolkit.getClass().getMethod("getSystemClipboard", null).invoke(toolkit, null); + Object contents = systemclipboard.getClass().getMethod("getContents", new Class[] { Object.class }). + invoke(systemclipboard, new Object[] { this }); + Class dataflavor = Class.forName("java.awt.datatransfer." + "DataFlavor"); + insert = (String) (contents.getClass().getMethod("getTransferData", new Class[] { dataflavor }). + invoke(contents, new Object[] { dataflavor.getField("stringFlavor").get(null) }));*/ + insert = (String) getToolkit().getSystemClipboard(). + getContents(this).getTransferData(DataFlavor.stringFlavor); + } catch (Exception exc) { + insert = clipboard; + } + // 0x1f) && (ckey < 0x7f)) || + ((ckey > 0x9f) && (ckey < 0xffff)) || + (multiline && (ckey == '\n'))) { + filtered.append(ckey); + } + } + if (filtered.length() != insert.length()) { + insert = filtered.toString(); + } + } + return changeField(component, text, insert, istart, iend, start, end); + } + + /** + * + */ + private boolean changeField(Object component, String text, String insert, + int movestart, int moveend, int start, int end) { + if ((insert == null) && (start == movestart) && (end == moveend)) { + return false; + } + movestart = Math.max(0, Math.min(movestart, text.length())); + moveend = Math.max(0, Math.min(moveend, text.length())); + if (insert != null) { + int min = Math.min(movestart, moveend); + set(component, "text", text.substring(0, min) + insert + + text.substring(Math.max(movestart, moveend))); + movestart = moveend = min + insert.length(); + invoke(component, "action"); + } + if (start != movestart) { setInteger(component, "start", movestart, 0); } + if (end != moveend) { setInteger(component, "end", moveend, 0); } + if ((insert != null) || (start != movestart) || (end != moveend)) { + validate(component); + } + return true; + } + + /** + * + */ + private boolean processList(Object component, boolean shiftdown, boolean controldown, + int keychar, int keycode, String itemname, String leafname) { + if ((keycode == KeyEvent.VK_UP) || + (keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_UP) || + (keycode == KeyEvent.VK_PAGE_DOWN) || + (keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_END)) { + Object lead = get(component, "lead"); + Object row = getListItem(component, component, + keycode, lead, itemname, leafname); + if (row != null) { + String selection = getString(component, "selection", "single"); + if (shiftdown && (selection != "single") && (lead != null)) { + extend(component, lead, row, itemname, leafname); + } + else if (!controldown) { + selectItem(component, row, itemname, leafname); + } + setLead(component, lead, row); + Rectangle r = getRectangle(row, "bounds"); + scrollToVisible(component, r.x, r.y, 0, r.height - 1); + return true; + } + } + else if (keychar == KeyEvent.VK_SPACE) { + select(component, get(component, "lead"), + itemname, leafname, shiftdown, controldown); //... + return true; + } + else if (controldown) { + if (((keycode == KeyEvent.VK_A) || (keycode == 0xBF)) && //KeyEvent.VK_SLASH + (getString(component, "selection", "single") != "single")) { + selectAll(component, true, itemname, leafname); + return true; + } + else if (keycode == 0xDC) { //KeyEvent.VK_BACK_SLASH + selectAll(component, false, itemname, leafname); + return true; + } + } + return false; + } + + /** + * + */ + private Object getListItem(Object component, Object scrollpane, + int keycode, Object lead, String itemname, String leafname) { + Object row = null; + if (keycode == KeyEvent.VK_UP) { + for (Object prev = get(component, itemname); prev != lead; + prev = getNextItem(component, prev, leafname)) { + row = prev; // component -> getParent(lead) + } + } + else if (keycode == KeyEvent.VK_DOWN) { + row = (lead == null) ? get(component, itemname) : + getNextItem(component, lead, leafname); + } + else if ((keycode == KeyEvent.VK_PAGE_UP) || + (keycode == KeyEvent.VK_PAGE_DOWN)) { + Rectangle view = getRectangle(scrollpane, ":view"); + Rectangle port = getRectangle(scrollpane, ":port"); + Rectangle rl = (lead != null) ? getRectangle(lead, "bounds") : null; + int vy = (keycode == KeyEvent.VK_PAGE_UP) ? + view.y : (view.y + port.height - 2); + if ((keycode == KeyEvent.VK_PAGE_UP) && + (rl != null) && (rl.y <= view.y)) { + vy -= port.height - 2; + } + if ((keycode == KeyEvent.VK_PAGE_DOWN) && + (rl != null) && (rl.y + rl.height >= view.y + port.height - 2)) { + vy += port.height - 2; + } + for (Object item = get(component, itemname); item != null; + item = getNextItem(component, item, leafname)) { + Rectangle r = getRectangle(item, "bounds"); + if (keycode == KeyEvent.VK_PAGE_UP) { + row = item; + if (r.y + r.height > vy) { break; } + } else { + if (r.y > vy) { break; } + row = item; + } + } + } + else if (keycode == KeyEvent.VK_HOME) { + row = get(component, itemname); + } + else if (keycode == KeyEvent.VK_END) { + for (Object last = lead; last != null; + last = getNextItem(component, last, leafname)) { + row = last; + } + } + return row; + } + + /** + * + */ + private void selectAll(Object component, + boolean selected, String itemname, String leafname) { + boolean changed = false; + for (Object item = get(component, itemname); + item != null; item = getNextItem(component, item, leafname)) { + if (setBoolean(item, "selected", selected, false)) { + repaint(component, null, item); changed = true; + } + } + set(component, "anchor", null); + if (changed) {invoke(component, "action"); } + } + + /** + * + */ + private void selectItem(Object component, + Object row, String itemname, String leafname) { + boolean changed = false; + for (Object item = get(component, itemname); + item != null; item = getNextItem(component, item, leafname)) { + if (setBoolean(item, "selected", (item == row), false)) { + repaint(component, null, item); changed = true; + } + } + set(component, "anchor", null); + if (changed) { invoke(component, "action"); } + } + + /** + * + */ + private void extend(Object component, Object lead, + Object row, String itemname, String leafname) { + Object anchor = get(component, "anchor"); + if (anchor == null) { set(component, "anchor", anchor = lead); } + char select = 'n'; boolean changed = false; + for (Object item = get(component, itemname); // anchor - row + item != null; item = getNextItem(component, item, leafname)) { + if (item == anchor) select = (select == 'n') ? 'y' : 'r'; + if (item == row) select = (select == 'n') ? 'y' : 'r'; + if (setBoolean(item, "selected", (select != 'n'), false)) { + repaint(component, null, item); changed = true; + } + if (select == 'r') select = 'n'; + } + if (changed) { invoke(component, "action"); } + } + + /** + * + */ + private void setLead(Object component, Object oldlead, Object lead) { + if (oldlead != lead) { //? + if (oldlead != null) { repaint(component, null, oldlead); } + set(component, "lead", lead); + repaint(component, null, lead); + } + } + + /*public void repaint(int x, int y, int width, int height) { + System.out.println("repaint(" + x + ", " + y + ", " + width + ", " + height + ")"); + super.repaint(x, y, width, height); + }*/ + + /** + * + */ + private void handleMouseEvent(int x, int y, int clickcount, + boolean shiftdown, boolean controldown, boolean popuptrigger, + int id, Object component, Object part) { + if (id == MouseEvent.MOUSE_ENTERED) { + setTimer(750L); + } + else if (id == MouseEvent.MOUSE_EXITED) { + hideTip(); + } + if (!getBoolean(component, "enabled", true)) { return; } + String classname = getClass(component); + if (("button" == classname) || ("checkbox" == classname)) { + if ((id == MouseEvent.MOUSE_ENTERED) || + (id == MouseEvent.MOUSE_EXITED) || + (id == MouseEvent.MOUSE_PRESSED) || + (id == MouseEvent.MOUSE_RELEASED)) { + if (id == MouseEvent.MOUSE_PRESSED) { + setFocus(component); + } + else if ((id == MouseEvent.MOUSE_RELEASED) && + (mouseinside == component)) { + if ("checkbox" == classname) { + changeCheck(component, true); + } + else invoke(component, "action"); + } + repaint(component); + } + } + else if ("combobox" == classname) { + boolean editable = getBoolean(component, "editable", true); + if (editable && (part == null)) { + Image icon = null; + int left = ((id == MouseEvent.MOUSE_PRESSED) && + ((icon = getIcon(component, "icon", null)) != null)) ? + icon.getWidth(this) : 0; + processField(x, y, clickcount, id, component, part, false, false, left); + } + else if (part != "icon") { // part = "down" + if (((id == MouseEvent.MOUSE_ENTERED) || + (id == MouseEvent.MOUSE_EXITED)) && (mousepressed == null)) { + if (editable) { repaint(component, "combobox", part); } + else { repaint(component); } + } + else if (id == MouseEvent.MOUSE_PRESSED) { + Object combolist = get(component, "combolist"); + if (combolist == null) { + setFocus(component); + repaint(component); + popup(component, classname); + } else { + closeup(component, combolist, null); + } + } + else if (id == MouseEvent.MOUSE_RELEASED) { + if (mouseinside != component) { + Object combolist = get(component, "combolist"); + closeup(component, combolist, + (mouseinside == combolist) ? insidepart : null); + } else { + repaint(component); + } + } + } + } + else if ("combolist" == classname) { + if (!processScroll(x, y, id, component, part)) { + if ((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)) { + if (part != null) { + // repaint previous inside + set(component, "inside", part); + repaint(component, classname, part); + } + } + else if ((id == MouseEvent.MOUSE_EXITED) || (id == DRAG_EXITED)) { + if (part != null) { + set(component, "inside", null); + repaint(component, classname, part); + } + } + else if (id == MouseEvent.MOUSE_RELEASED) { + closeup(get(component, "combobox"), component, part); + } + } + } + else if (("textfield" == classname) || ("passwordfield" == classname)) { + processField(x, y, clickcount, id, component, part, + false, ("passwordfield" == classname), 0); + } + else if ("textarea" == classname) { + if (!processScroll(x, y, id, component, part)) { + processField(x, y, clickcount, id, component, part, true, false, 0); + } + } + //java> + else if ("desktop" == classname) { + if (id == MouseEvent.MOUSE_ENTERED) { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + else if (id == MouseEvent.MOUSE_EXITED) { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + //MouseEvent.MOUSE_ENTERED) || + (id == MouseEvent.MOUSE_EXITED) || + (id == MouseEvent.MOUSE_PRESSED) || + (id == MouseEvent.MOUSE_RELEASED)) { + if (id == MouseEvent.MOUSE_PRESSED) { + setFocus(component); + if (processSpin(component, part)) { setTimer(375L); } + //settext: start end selection, parse exception... + } + else { + if (id == MouseEvent.MOUSE_RELEASED) { + setTimer(0L); + } + } + repaint(component, classname, part); + } + } + } + else if ("tabbedpane" == classname) { + if ((id == MouseEvent.MOUSE_ENTERED) || + (id == MouseEvent.MOUSE_EXITED)) { + if ((part != null) && getBoolean(part, "enabled", true) && + (getInteger(component, "selected", 0) != getIndex(component, "tab", part))) { + repaint(component, "tabbedpane", part); + } + } + else if ((part != null) && (id == MouseEvent.MOUSE_PRESSED) && + getBoolean(part, "enabled", true)) { + int selected = getInteger(component, "selected", 0); + int current = getIndex(component, "tab", part); + if (selected == current) { + setFocus(component); + repaint(component, "tabbedpane", part); + } + else { + setInteger(component, "selected", current, 0); + //Object tabcontent = getItemImpl(component, "component", current); + //setFocus((tabcontent != null) ? tabcontent : component); + setNextFocusable(component, false); + repaint(component); + invoke(component, "action"); + } + } + } + else if ("slider" == classname) { + if ((id == MouseEvent.MOUSE_PRESSED) || + (id == MouseEvent.MOUSE_DRAGGED)) { + if (id == MouseEvent.MOUSE_PRESSED) { + setReference(component, block / 2, block / 2); + setFocus(component); + } + int minimum = getInteger(component, "minimum", 0); + int maximum = getInteger(component, "maximum", 100); + int value = getInteger(component, "value", 50); + Rectangle bounds = getRectangle(component, "bounds"); + boolean horizontal = ("vertical" != get(component, "orientation")); + int newvalue = minimum + + (horizontal ? (mousex - referencex) : (mousey - referencey)) * + (maximum - minimum) / + ((horizontal ? bounds.width : bounds.height) - block); //... +0.5 + newvalue = Math.max(minimum, Math.min(newvalue, maximum)); + if (value != newvalue) { + setInteger(component, "value", newvalue, 50); + invoke(component, "action"); + } + if ((value != newvalue) || (id == MouseEvent.MOUSE_PRESSED)) { + repaint(component); + } + } + } + else if ("splitpane" == classname) { + if (id == MouseEvent.MOUSE_PRESSED) { + setReference(component, 2, 2); + } + else if (id == MouseEvent.MOUSE_DRAGGED) { + int divider = getInteger(component, "divider", -1); + boolean horizontal = ("vertical" != get(component, "orientation")); + int moveto = horizontal ? (mousex - referencex) : + (mousey - referencey); + Rectangle bounds = getRectangle(component, "bounds"); + moveto = Math.max(0, Math.min(moveto, + Math.abs(horizontal ? bounds.width : bounds.height) - 5)); + if (divider != moveto) { + setInteger(component, "divider", moveto, -1); + validate(component); + } + } + //java> + else if ((id == MouseEvent.MOUSE_ENTERED) && (mousepressed == null)) { + boolean horizontal = ("vertical" != get(component, "orientation")); + setCursor(Cursor.getPredefinedCursor(horizontal ? + Cursor.E_RESIZE_CURSOR : Cursor.S_RESIZE_CURSOR)); + } + else if (((id == MouseEvent.MOUSE_EXITED) && (mousepressed == null)) || + ((id == MouseEvent.MOUSE_RELEASED) && (mouseinside != component))) { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + //MouseEvent.MOUSE_PRESSED)|| + ((id == MouseEvent.MOUSE_DRAGGED) && + !shiftdown && !controldown)) && + !popuptrigger) { // e.getClickCount() + Rectangle view = getRectangle(component, ":view"); + Rectangle viewport = getRectangle(component, ":port"); + int my = mousey + view.y - referencey; + String itemname = ("list" == classname) ? "item" : + (("table" == classname) ? "row" : "node"); + String subitem = ("tree" == classname) ? "node" : null; + for (Object item = get(component, itemname); item != null;) { + Rectangle r = getRectangle(item, "bounds"); + if (my < r.y + r.height) { + if (id == MouseEvent.MOUSE_DRAGGED) { //!!! + scrollToVisible(component, r.x, r.y, 0, r.height - 1); + } + else if ("tree" == classname) { + int mx = mousex + view.x - referencex; + if (mx < r.x) { + if ((mx >= r.x - block) && (get(item, "node") != null)) { + boolean expanded = getBoolean(item, "expanded", true); + setBoolean(item, "expanded", !expanded, true); + selectItem(component, item, "node", "node"); + setLead(component, get(component, "lead"), item); + setFocus(component); + validate(component); + invoke(component, expanded ? "collapse" : "expand"); //item + } + break; + } + } + if ((id != MouseEvent.MOUSE_DRAGGED) || + !getBoolean(item, "selected", false)) { + select(component, item, itemname, subitem, shiftdown, controldown); + if (id != MouseEvent.MOUSE_DRAGGED) { + if (setFocus(component)) { repaint(component, classname, item); } //? + } + } + break; + } + item = getNextItem(component, item, subitem); + } + } + } + } + else if ("menubar" == classname) { + Object selected = get(component, "selected"); + if (((id == MouseEvent.MOUSE_ENTERED) || (id == MouseEvent.MOUSE_EXITED)) && + (part != null) && (selected == null)) { + repaint(component, classname, part); + } + else if ((part != null) && ((selected == null) ? + (id == MouseEvent.MOUSE_PRESSED) : + ((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)))) { + set(component, "selected", part); + popup(component, classname); + repaint(component, classname, part); + } + else if ((id == MouseEvent.MOUSE_PRESSED) && (selected != null)) { + closeup(component); + } + else if (id == MouseEvent.MOUSE_RELEASED) { + if ((part != insidepart) && + ((insidepart == null) || (getClass(insidepart) != "menu"))) { + if ((insidepart != null) && getBoolean(insidepart, "enabled", true)) { + if (getClass(insidepart) == "checkboxmenuitem") { + changeCheck(insidepart, false); + } + else invoke(insidepart, "action"); + } + closeup(component); + } + } + } + else if ("popupmenu" == classname) { + if (part != null) { + if ((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)) { + set(component, "selected", part); + popup(component, classname); + repaint(component, classname, part); + } + else if (id == MouseEvent.MOUSE_RELEASED) { + if ((insidepart == null) || (getClass(insidepart) != "menu")) { + Object menubar = part; + do { + menubar = getParent(menubar); + } while (getClass(menubar) != "menubar"); + if ((insidepart != null) && getBoolean(insidepart, "enabled", true)) { + if (getClass(insidepart) == "checkboxmenuitem") { + changeCheck(insidepart, false); + } + else invoke(insidepart, "action"); + } + closeup(menubar); + } + } + else if ((id == MouseEvent.MOUSE_EXITED) || (id == DRAG_EXITED)) { + if (getClass(part) != "menu") { + set(component, "selected", null); + } + repaint(component, classname, part); + } + } + } + else if ("dialog" == classname) { + if (part == "header") { + if (id == MouseEvent.MOUSE_PRESSED) { + referencex = mousex; referencey = mousey; + if (!getBoolean(component, "modal", false) && + (get(content, "component") != component)) { + removeItemImpl(content, "component", component); + insertItem(content, "component", component, 0); + set(component, ":parent", content); + repaint(component); + } + } + else if (id == MouseEvent.MOUSE_DRAGGED) { + Rectangle bounds = getRectangle(component, "bounds"); + int dx = mousex - referencex; int dy = mousey - referencey; + repaint(component, + bounds.x + Math.min(0, dx), bounds.y + Math.min(0, dy), + bounds.width + Math.abs(dx), bounds.height + Math.abs(dy)); + bounds.x += dx; bounds.y += dy; + referencex = mousex; referencey = mousey; + } + } + } + } + + /** + * + */ + private void setReference(Object component, int x, int y) { + referencex = x; referencey = y; + for (; component != null; component = getParent(component)) { + Rectangle bounds = getRectangle(component, "bounds"); + referencex += bounds.x; referencey += bounds.y; + } + } + + /** + * + */ + private void select(Object component, Object row, String first, + String child, boolean shiftdown, boolean controldown) { + String selection = getString(component, "selection", "single"); + Object lead = null; + if (shiftdown && (selection != "single") && + ((lead = get(component, "lead")) != null)) { + extend(component, lead, row, first, child); + } + else { + if (controldown && (selection == "multiple")) { + setBoolean(row, "selected", + !getBoolean(row, "selected", false), false); + repaint(component, null, row); + invoke(component, "action"); + set(component, "anchor", null); + } + else if (controldown && getBoolean(row, "selected", false)) { + for (Object item = row; + item != null; item = getNextItem(component, item, child)) { + if (setBoolean(item, "selected", false, false)) { + repaint(component, null, item); + } + } + invoke(component, "action"); + set(component, "anchor", null); + } + else { + selectItem(component, row, first, child); + } + } + setLead(component, (lead != null) ? lead : get(component, "lead"), row); + } + + /** + * + */ + private Object getNextItem(Object component, + Object item, String subitem) { + if (subitem == null) { return get(item, ":next"); } + Object next = get(item, subitem); + if ((next == null) || !getBoolean(item, "expanded", true)) { + while ((item != component) && ((next = get(item, ":next")) == null)) { + item = getParent(item); + } + } + return next; + } + + /** + * + */ + private void processField(int x, int y, int clickcount, + int id, Object component, + Object part, boolean multiline, boolean hidden, int left) { + if (id == MouseEvent.MOUSE_PRESSED) { + setReference(component, 2 + left, 2); + int mx = x - referencex; + int my = 0; + if (!multiline) { + mx += getInteger(component, "offset", 0); + } else { + Rectangle view = getRectangle(component, ":view"); + mx += view.x - 1; + my = y - referencey + view.y - 1; + } + int caretstart = getCaretLocation(component, mx, my, hidden); + int caretend = caretstart; + //java> + if (clickcount > 1) { + String text = getString(component, "text", ""); + while ((caretstart > 0) && ((clickcount == 2) ? + Character.isLetterOrDigit(text.charAt(caretstart - 1)) : + (text.charAt(caretstart - 1) != '\n'))) { caretstart--; } + while ((caretend < text.length()) && ((clickcount == 2) ? + Character.isLetterOrDigit(text.charAt(caretend)) : + (text.charAt(caretend) != '\n'))) { caretend++; } + } + //MouseEvent.MOUSE_DRAGGED) { + int mx = x - referencex; + int my = 0; + if (!multiline) { + mx += getInteger(component, "offset", 0); + } else { + Rectangle view = getRectangle(component, ":view"); + mx += view.x - 1; + my = y - referencey + view.y - 1; + } + int dragcaret = getCaretLocation(component, mx, my, hidden); + if (dragcaret != getInteger(component, "end", 0)) { + setInteger(component, "end", dragcaret, 0); + validate(component); // caret check only + } + } + //java> + else if ((id == MouseEvent.MOUSE_ENTERED) && (mousepressed == null)) { + setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + } + else if (((id == MouseEvent.MOUSE_EXITED) && (mousepressed == null)) || + ((id == MouseEvent.MOUSE_RELEASED) && + ((mouseinside != component) || (insidepart != null)))) { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + //FontMetrics fm = getFontMetrics(getFont()); + for (int i = 0, j = 0; true; i = j + 1) { + j = text.indexOf('\n', i); + if ((j == -1) || y < fm.getHeight()) { + if (j == -1) { j = text.length(); } + for (int k = i; k < j; k++) { + int charwidth = fm.charWidth(hidden ? '*' : text.charAt(k)); + if (x <= (charwidth / 2)) { + return k; + } + x -= charwidth; + } + return j; + } + y -= fm.getHeight(); + } + } + + /** + * + */ + private boolean processScroll(int x, int y, + int id, Object component, Object part) { + if ((part == "up") || (part == "down") || + (part == "left") || (part == "right")) { + if ((id == MouseEvent.MOUSE_ENTERED) || + (id == MouseEvent.MOUSE_EXITED) || + (id == MouseEvent.MOUSE_PRESSED) || + (id == MouseEvent.MOUSE_RELEASED)) { + if (id == MouseEvent.MOUSE_PRESSED) { + if (processScroll(component, part)) { + setTimer(300L); return true; + } + } + else { + if (id == MouseEvent.MOUSE_RELEASED) { setTimer(0L); } + repaint(component, null, part); + } + } + } + else if ((part == "uptrack") || (part == "downtrack") || + (part == "lefttrack") || (part == "righttrack")) { + if (id == MouseEvent.MOUSE_PRESSED) { + if (processScroll(component, part)) { + setTimer(300L); + } + } + else if (id == MouseEvent.MOUSE_RELEASED) { + setTimer(0L); + } + } + else if ((part == "vknob") || (part == "hknob")) { + if (id == MouseEvent.MOUSE_PRESSED) { + Rectangle port = getRectangle(component, ":port"); + Rectangle view = getRectangle(component, ":view"); + if (part == "hknob") { + referencex = x - + view.x * (port.width - 2 * block) / view.width; + } else { + referencey = y - + view.y * (port.height - 2 * block) / view.height; + } + } + else if (id == MouseEvent.MOUSE_DRAGGED) { + Rectangle port = getRectangle(component, ":port"); + Rectangle view = getRectangle(component, ":view"); + if (part == "hknob") { + int viewx = (x - referencex) * + view.width / (port.width - 2 * block); + viewx = Math.max(0, Math.min(viewx, view.width - port.width + 2)); + if (view.x != viewx) { + view.x = viewx; + repaint(component, null, "horizontal"); + } + } else { + int viewy = (y - referencey) * + view.height / (port.height - 2 * block); + viewy = Math.max(0, Math.min(viewy, view.height - port.height + 2)); + if (view.y != viewy) { + view.y = viewy; + repaint(component, null, "vertical"); + } + } + } + } + else if (part == "corner") { + part = "corner"; // compiler bug + } + else { + if (id == MouseEvent.MOUSE_PRESSED) { + Rectangle port = getRectangle(component, ":port"); + setReference(component, port.x + 1, port.y + 1); + } + return false; + } + return true; + } + + /** + * + */ + private boolean processScroll(Object component, Object part) { + Rectangle view = getRectangle(component, ":view"); + Rectangle port = ((part == "left") || (part == "up")) ? null : + getRectangle(component, ":port"); + int dx = 0; int dy = 0; + if (part == "left") { dx = -10; } + else if (part == "lefttrack") { dx = -port.width; } + else if (part == "right") { dx = 10; } + else if (part == "righttrack") { dx = port.width; } + else if (part == "up") { dy = -10; } + else if (part == "uptrack") { dy = -port.height; } + else if (part == "down") { dy = 10; } + else if (part == "downtrack") { dy = port.height; } + if (dx != 0) { + dx = (dx < 0) ? Math.max(-view.x, dx) : + Math.min(dx, view.width - port.width + 2 - view.x); + } + else if (dy != 0) { + dy = (dy < 0) ? Math.max(-view.y, dy) : + Math.min(dy, view.height - port.height + 2 - view.y); + } + else return false; + view.x += dx; view.y += dy; + repaint(component, null, (dx != 0) ? "horizontal" : "vertical"); + return (((part == "left") || (part == "lefttrack")) && (view.x > 0)) || + (((part == "right") || (part == "righttrack")) && + (view.x < view.width - port.width + 2)) || + (((part == "up") || (part == "uptrack")) && (view.y > 0)) || + (((part == "down") || (part == "downtrack")) && + (view.y < view.height - port.height + 2)); + } + + /** + * + */ + private boolean processSpin(Object component, Object part) { + String text = getString(component, "text", null); + if (text != null) { + try { + String value = String.valueOf( + Integer.parseInt(text) + ((part == "up") ? 1 : -1)); + setString(component, "text", value, null); + setInteger(component, "start", value.length(), 0); + setInteger(component, "end", 0, 0); + repaint(component, "spinbox", null); + invoke(component, "action"); + return true; + } catch (NumberFormatException nfe) {} + } + return false; + } + + //java> + /*public void setEventHandler(Object component, Object eventhandler) { + set(component, ":handler", eventhandler); + }*/ + + /** + * + */ + private void invoke(Object component, String event) { + Method method = (Method) get(component, event); + if (method != null) { + try { + method.invoke(this, null); + } catch (InvocationTargetException ite) { + ite.getTargetException().printStackTrace(); + } catch (Exception exc) { + exc.printStackTrace(); + } + } + } + //Rectangle bounds = getRectangle(component, "bounds"); + if ((bounds == null) || !(bounds.contains(x, y))) { return false; } //java + //midp if ((bounds == null) || (x < bounds.x) || (x - bounds.x >= bounds.width) || + //midp (y < bounds.y) || (y - bounds.y >= bounds.height)) { return false; } + mouseinside = component; + x -= bounds.x; y -= bounds.y; + String classname = getClass(component); + + if ("combobox" == classname) { + if (getBoolean(component, "editable", true) && (x <= bounds.width - block)) { + Image icon = getIcon(component, "icon", null); + insidepart = ((icon != null) && (x <= 2 + icon.getWidth(this))) ? + "icon" : null; + } else { + insidepart = "down"; + } + } + else if ("combolist" == classname) { + if (!findScrollPane(component, x, y, bounds)) { + y += getRectangle(component, ":view").y; + for (Object choice = get(get(component, "combobox"), "choice"); + choice != null; choice = get(choice, ":next")) { + Rectangle r = getRectangle(choice, "bounds"); + if ((y >= r.y) && (y < r.y + r.height)) { + insidepart = choice; break; + } + } + } + } + else if ("textarea" == classname) { + findScrollPane(component, x, y, bounds); + } + else if ("tabbedpane" == classname) { + Object tabcontent = getItemImpl(component, + "component", getInteger(component, "selected", 0)); + if ((tabcontent == null) || !findComponent(tabcontent, x, y)) { + for (Object comp = get(component, "tab"); + comp != null; comp = get(comp, ":next")) { + Rectangle r = getRectangle(comp, "bounds"); + if (r.contains(x, y)) { //java + //midp if ((x >= r.x) && (x - r.x < r.width) && (y >= r.y) && (y - r.y < r.height)) { + insidepart = comp; break; + } + } + } + } + else if (("panel" == classname) || ("desktop" == classname) || + ("dialog" == classname)) { + if (("dialog" == classname) && + (y < 4 + getInteger(component, "titleheight", 0))) { + insidepart = "header"; + } else { + for (Object comp = get(component, "component"); + comp != null; comp = get(comp, ":next")) { + if (findComponent(comp, x, y)) { break; } + if (("desktop" == classname) && + getBoolean(comp, "modal", false)) { break; } // && dialog + } + } + } + else if ("spinbox" == classname) { + insidepart = (x <= bounds.width - block) ? null : + ((y <= bounds.height / 2) ? "up" : "down"); + } + else if ("splitpane" == classname) { + Object comp1 = get(component, "component"); + if (comp1 != null) { + if (!findComponent(comp1, x, y)) { + Object comp2 = get(comp1, ":next"); + if (comp2 != null) { + findComponent(comp2, x, y); + } + } + } + } + else if ("list" == classname) { + findScrollPane(component, x, y, bounds); + } + else if ("table" == classname) { + findScrollPane(component, x, y, bounds); + } + else if ("tree" == classname) { + findScrollPane(component, x, y, bounds); + } + else if ("menubar" == classname) { + for (Object menu = get(component, "menu"); + menu != null; menu = get(menu, ":next")) { + Rectangle r = getRectangle(menu, "bounds"); + if ((x >= r.x) && (x < r.x + r.width)) { + insidepart = menu; break; + } + } + } + else if ("popupmenu" == classname) { + for (Object menu = get(get(component, "menu"), "menu"); + menu != null; menu = get(menu, ":next")) { + Rectangle r = getRectangle(menu, "bounds"); + if ((y >= r.y) && (y < r.y + r.height)) { + insidepart = menu; break; + } + } + } + return true; + } + + /** + * + */ + private boolean findScrollPane(Object component, + int x, int y, Rectangle bounds) { + Rectangle port = getRectangle(component, ":port"); + if ((x < port.x) || (y < port.y) || + ((x >= port.x + port.width) && (y >= port.y + port.height))) { + insidepart = "corner"; + } + else if ((x >= port.x + port.width) || (y >= port.y + port.height)) { + boolean horizontal = (y >= port.y + port.height); + int p = horizontal ? (x - port.x) : (y - port.y); + int portsize = horizontal ? port.width : port.height; + int button = Math.min(block, portsize / 2); + if (p < button) { + insidepart = horizontal ? "left" : "up"; + } + else if (p >= portsize - button) { + insidepart = horizontal ? "right" : "down"; + } + else { + Rectangle view = getRectangle(component, ":view"); + int viewp = horizontal ? view.x : view.y; + int viewsize = horizontal ? view.width : view.height; + int track = portsize - (2 * button); + int knob = Math.min(track, + Math.max(track * (portsize - 2) / viewsize, 6)); + int decrease = viewp * (track - knob) / (viewsize - portsize + 2); + if (p < button + decrease) { + insidepart = horizontal ? "lefttrack" : "uptrack"; + } + else if (p < button + decrease + knob) { + insidepart = horizontal ? "hknob" : "vknob"; + } + else { + insidepart = horizontal ? "righttrack" : "downtrack"; + } + } + } + else { return false; } + return true; + } + + /** + * + */ + private void repaint(Object component, Object classname, Object part) { + Rectangle b = getRectangle(component, "bounds"); + if ((classname == "combobox") || (classname == "spinbox")) { + boolean down = (part == "up") || (part == "down"); // else text + repaint(component, down ? (b.x + b.width - block) : b.x, b.y, + down ? block : (b.width - block), b.height); + } + //else if (classname == "dialog") {} + //int titleheight = getInteger(component, "titleheight", 0); + //else if (classname == "splitpane") {} + else if ((classname == "tabbedpane") || + (classname == "menubar") || (classname == "popupmenu")) { + Rectangle r = getRectangle(part, "bounds"); + repaint(component, b.x + r.x, b.y + r.y, + (classname == "popupmenu") ? b.width : r.width, r.height); + } + else //if ((classname == "combolist") || (classname == "textarea") || + {//(classname == "list") || (classname == "table") || (classname == "tree")) { + Rectangle port = getRectangle(component, ":port"); + if (part == "left") { + repaint(component, b.x + port.x, b.y + b.height - block, block, block); + } + else if (part == "right") { + repaint(component, b.x + port.x + port.width - block, b.y + b.height - block, block, block); + } + else if (part == "up") { + repaint(component, b.x + b.width - block, b.y + port.y, block, block); + } + else if (part == "down") { + repaint(component, b.x + b.width - block, b.y + port.y + port.height - block, block, block); + } + else if (part == "horizontal") { // horizontaly center part + repaint(component, b.x + port.x, b.y, port.width, b.height); + } + else if (part == "vertical") { + repaint(component, b.x, b.y + port.y, b.width, port.height); + } + else if (part == "text") { //textarea + repaint(component, b.x + port.x, b.y + port.y, port.width, port.height); + } + else { + Rectangle view = getRectangle(component, ":view"); + Rectangle r = getRectangle(part, "bounds"); + if ((r.y + r.height >= view.y) && (r.y <= view.y + port.height)) { + repaint(component, b.x + port.x, b.y + port.y - view.y + 1 + r.y, + port.width, r.height); + //? need cut item rectangle above/bellow viewport + } + } + } + } + + /** + * + */ + private void validate(Object component) { + repaint(component); + Rectangle bounds = getRectangle(component, "bounds"); + bounds.width = -1 * Math.abs(bounds.width); + } + + /** + * + */ + private void repaint(Object component) { + Rectangle bounds = getRectangle(component, "bounds"); + if (bounds != null) { + repaint(component, bounds.x, bounds.y, bounds.width, bounds.height); + } + } + + /** + * + */ + private void repaint(Object component, int x, int y, int width, int height) { + while ((component = getParent(component)) != null) { + Rectangle bounds = getRectangle(component, "bounds"); + x += bounds.x; + y += bounds.y; + } + repaint(x, y, width, height); + } + + /*private void clip(Graphics g, + Rectangle clip, int x, int y, int width, int height) { + int x1 = Math.max(clip.x, x); + int y1 = Math.max(clip.y, y); + int x2 = Math.min(clip.x + clip.width, x + width); + int y2 = Math.min(clip.y + clip.height, y + height); + g.setClip(x1, y1, x2 - x1, y2 - y1); + }*/ + + /** + * + */ + public boolean requestFocus(Object component) { + if (checkFocusable(component, true)) { + setFocus(component); return true; + } + return false; + } + + /** + * + */ + private boolean setFocus(Object component) { + if (!focusinside) { //java + requestFocus(); //java + } //java + if (focusowner != component) { + Object focused = focusowner; + focusowner = component; + if (focused != null) { + //mouseEvent(null, FocusEvent.FOCUS_LOST, focused, null, null); + repaint(focused); + //focusGained(component); + } + return true; + } + return false; + } + + /** + * @return next focusable component is found (not the first of the desktop/dialog) + */ + private boolean setNextFocusable(Object current, boolean outgo) { + boolean consumed = true; + for (Object next = null, component = current; true; component = next) { + next = get(component, "component"); // check first subcomponent + if (next == null) { next = get(component, ":next"); } // check next component + while (next == null) { // find the next of the parents, or the topmost + component = getParent(component); // current is not on the desktop + if (component == null) { return false; } + if ((component == content) || ((getClass(component) == "dialog") && + (!outgo || getBoolean(component, "modal", false)))) { + consumed = false; // find next focusable but does not consume event + next = component; // the topmost (desktop or modal dialog) + } + else { + next = get(component, ":next"); + } + } + if (next == current) { return false; } // one fucusable, no loop + if (checkFocusable(next, false)) { + setFocus(next); + return consumed; + } + } + } + + //java> + /** + * @return previous focusable component is found (not the last of the desktop/dialog) + */ + private boolean setPreviousFocusable(Object component, boolean outgo) { + for (int i = 0; i < 2; i++) { // 0 is backward direction + Object previous = getPreviousFocusable(component, null, true, false, (i == 0), outgo); + if (previous != null) { + setFocus(previous); + return (i == 0); + } + } + return false; + } + + /** + * For the starting component search its parent direction for a focusable component, and then + * its next component (if not search backward from the component).
+ * For its parent components check its first component, the current one, and its parent direction + * (backward search), or its parent, then next component (forward direction).
+ * For the rest components check the next, then the first subcomponent direction, and finally + * check whether the component is focusable. + */ + private Object getPreviousFocusable(Object component, + Object block, boolean start, boolean upward, boolean backward, boolean outgo) { + Object previous = null; + if ((component != null) && (component != block)) { + boolean go = ((getClass(component) != "dialog") || + (outgo && !getBoolean(component, "modal", false))); + if (!start && !upward && go) { + previous = getPreviousFocusable(get(component, ":next"), block, false, false, backward, outgo); + } + if ((previous == null) && ((upward && backward) || (!start && !upward))) { + previous = getPreviousFocusable(get(component, "component"), block, false, false, backward, outgo); + if ((previous == null) && checkFocusable(component, false)) { + previous = component; + } + } + if ((previous == null) && (start || upward) && go) { + previous = getPreviousFocusable(getParent(component), component, false, true, backward, outgo); + } + if ((previous == null) && (start || upward) && !backward && go) { + previous = getPreviousFocusable(get(component, ":next"), block, false, false, backward, outgo); + } + } + return previous; + } + // " + path + " " + inputstream); + } catch (Throwable e) {} //java + //if (inputstream == null) { // applet code + // inputstream = new URL(getCodeBase(), path).openStream(); + //} + return parse(inputstream); //, Object handler + } + + /** + * + */ + public Object parse(InputStream inputstream) throws Exception { + return parse(inputstream, true); + } + + /** + * + */ + protected void parseXML(InputStream inputstream) throws Exception { + parse(inputstream, false); + } + + /** + * + */ + protected void startElement(String name, Hashtable attributelist) {} + + /** + * + */ + protected void characters(String text) {} + + /** + * + */ + protected void endElement() {} + + /** + * + */ + private Object parse(InputStream inputstream, boolean validate) throws Exception { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputstream)); //java + //midp InputStreamReader reader = new InputStreamReader(inputstream); + Object[] parentlist = null; + Object current = null; + Hashtable attributelist = null; + StringBuffer text = new StringBuffer(); + for (int c = reader.read(); c != -1;) { + if (c == '<') { + if ((c = reader.read()) == '/') { //endtag + if (text.length() > 0) { + if (text.charAt(text.length() - 1) == ' ') { + text.setLength(text.length() - 1); + } + if (!validate) { + characters(text.toString()); + }// else { + //addContent(current, text.toString()); + //} + text.setLength(0); + } + String tagname = (String) parentlist[2]; //getClass(current); + for (int i = 0; i < tagname.length(); i++) { // current-tag + if ((c = reader.read()) != tagname.charAt(i)) { + throw new IllegalArgumentException(tagname); + } + } + while (" \t\n\r".indexOf(c = reader.read()) != -1); // whitespace + if (c != '>') throw new IllegalArgumentException(); // '>' + c = reader.read(); + if (!validate) { endElement(); } + if (parentlist[0] == null) { + reader.close(); + return current; + } + current = parentlist[0]; + parentlist = (Object[]) parentlist[1]; + } + else { //start or standalone tag + boolean instruction = (c == '?'); // Processing Instructions + if (c == '!') { while ((c = reader.read()) != '>'); continue; } // DOCTYPE + if (instruction) { c = reader.read(); } + text.setLength(0); + boolean iscomment = false; + while (">/ \t\n\r".indexOf(c) == -1) { + text.append((char) c); + if ((text.length() == 3) && (text.charAt(0) == '!') && + (text.charAt(1) == '-') && (text.charAt(2) == '-')) { + int m = 0; + while (true) { + c = reader.read(); + if (c == '-') { m++; } + else if ((c == '>') && (m >= 2)) { break; } + else { m = 0; } + } + iscomment = true; + } + c = reader.read(); + } + if (iscomment) { continue; } + if (!instruction) { + String tagname = text.toString(); + parentlist = new Object[] { current, parentlist, tagname }; + if (validate) { + current = (current != null) ? + addElement(current, tagname) : create(tagname); + } else { + current = tagname; + } + } + text.setLength(0); + while (true) { + boolean whitespace = false; + while (" \t\n\r".indexOf(c) != -1) { + c = reader.read(); + whitespace = true; + } + if (c == '>') { + if (instruction) throw new IllegalArgumentException(); // '?>' + if (!validate) { + startElement((String) current, attributelist); attributelist = null; + } + c = reader.read(); + break; + } + else if (c == '/') { + if (instruction) throw new IllegalArgumentException(); // '?>' + if ((c = reader.read()) != '>') { + throw new IllegalArgumentException(); // '>' + } + if (!validate) { + startElement((String) current, attributelist); attributelist = null; + endElement(); + } + if (parentlist[0] == null) { + reader.close(); + return current; + } + current = parentlist[0]; + parentlist = (Object[]) parentlist[1]; + c = reader.read(); + break; + } + else if (instruction && (c == '?')) { + if ((c = reader.read()) != '>') { + throw new IllegalArgumentException(); // '>' + } + c = reader.read(); + break; + } + else if (whitespace) { + while ("= \t\n\r".indexOf(c) == -1) { + text.append((char) c); + c = reader.read(); + } + String key = text.toString(); + text.setLength(0); + while (" \t\n\r".indexOf(c) != -1) c = reader.read(); + if (c != '=') throw new IllegalArgumentException(); + while (" \t\n\r".indexOf(c = reader.read()) != -1); + char quote = (char) c; + if ((c != '\"') && (c != '\'')) throw new IllegalArgumentException(); + while (quote != (c = reader.read())) { + if (c == '&') { + StringBuffer eb = new StringBuffer(); + while (';' != (c = reader.read())) { eb.append((char) c); } + String entity = eb.toString(); + if ("lt".equals(entity)) { text.append('<'); } + else if ("gt".equals(entity)) { text.append('>'); } + else if ("amp".equals(entity)) { text.append('&'); } + else if ("quot".equals(entity)) { text.append('"'); } + else if ("apos".equals(entity)) { text.append('\''); } + else if (entity.startsWith("#")) { + text.append((char) Integer.parseInt(entity.substring(1))); + } + else throw new IllegalArgumentException("unknown " + "entity " + entity); + } + else text.append((char) c); + } + if (!instruction) { + if (validate) { + addAttribute(current, key, text.toString()); + } else { + if (attributelist == null) { attributelist = new Hashtable(); } + attributelist.put(key, text.toString()); + } + } + text.setLength(0); + c = reader.read(); + } + else throw new IllegalArgumentException(); + } + } + } + else { + if (" \t\n\r".indexOf(c) != -1) { + if ((text.length() > 0) && (text.charAt(text.length() - 1) != ' ')) { + text.append(' '); + } + } + else { + text.append((char) c); + } + c = reader.read(); + } + } + throw new IllegalArgumentException(); + } + + /** + * Convert entities. + */ + private static String convert(StringBuffer text) { + return null; + } + + /*private InputStream inputreader; + private byte[] data; + private int inputfrom = 0, inputto = 0; + private int read() throws Exception { + if (data == null) { data = new byte[1024]; } + if (inputfrom >= inputto) { + inputfrom = 0; + inputto = inputreader.read(data); + } + inputfrom++; + return data[inputfrom - 1]; + }*/ + + /** + * + */ + private void addImpl(Object parent, Object component, int index) { + String parentclass = getClass(parent); + String classname = getClass(component); + //System.out.println("add " + classname + " -> " + parentclass); + if ((("combobox" == parentclass) && ("choice" == classname)) || + (("tabbedpane" == parentclass) && ("tab" == classname)) || + (("list" == parentclass) && ("item" == classname)) || + (("table" == parentclass) && (("column" == classname) || ("row" == classname))) || + (("row" == parentclass) && ("cell" == classname)) || + ((("tree" == parentclass) || ("node" == parentclass)) && ("node" == classname)) || + (("menubar" == parentclass) && ("menu" == classname))) { + classname = classname; // compiler bug + } + else if (("menu" == parentclass) && (("menu" == classname) || ("menuitem" == classname) || + ("checkboxmenuitem" == classname) || ("separator" == classname))) { + classname = "menu"; + } + else if (("panel" == parentclass) || ("desktop" == parentclass) || + ("splitpane" == parentclass) || ("dialog" == parentclass) || + ("tabbedpane" == parentclass)) { + while ("component" != classname) { + String extendclass = null; + for (int i = 0; i < dtd.length; i += 3) { + if (classname == dtd[i]) { + extendclass = (String) dtd[i + 1]; break; + } + } + if (extendclass == null) throw new IllegalArgumentException(classname + " not component"); + classname = extendclass; + } + } + else throw new IllegalArgumentException(classname + " add " + parentclass); + insertItem(parent, (String) classname, component, index); + set(component, ":parent", parent); + //if (parent == content) System.out.println(getClass(parent) + ".add(" + getClass(component) + ") : " + classname); + } + + /** + * + */ + private Object addElement(Object parent, String name) { + //System.out.println("create '" + name + "'"); + Object component = create(name); + addImpl(parent, component, -1); + return component; + } + + /** + * + */ + private void addAttribute(Object component, String key, String value) { + //System.out.println("attribute '" + key + "'='" + value + "'"); + Object[] definition = getDefinition(component, key, null); + key = (String) definition[1]; + if ("string" == definition[0]) { + setString(component, key, value, (String) definition[3]); + } + else if ("choice" == definition[0]) { + String[] values = (String[]) definition[3]; + setChoice(component, key, value, values, values[0]); + } + else if ("boolean" == definition[0]) { + if ("true".equals(value)) { + if (definition[3] == Boolean.FALSE) { + set(component, key, Boolean.TRUE); + } + } + else if ("false".equals(value)) { + if (definition[3] == Boolean.TRUE) { + set(component, key, Boolean.FALSE); + } + } + else throw new IllegalArgumentException(value); + } + else if ("integer" == definition[0]) { + set(component, key, Integer.valueOf(value)); + } + else if ("icon" == definition[0]) { + set(component, key, getIcon(value)); + } + else if ("method" == definition[0]) { + try { //java + set(component, key, getClass().getMethod(value, null)); //java + } catch (Exception exc) { System.err.println(value); exc.printStackTrace(); } //java + //midp setMethod(component, key, value); + } + //java> + else if ("bean" == definition[0]) { + try { + set(component, key, (Component) Class.forName(value).newInstance()); + } catch (Exception exc) { System.err.println(value); exc.printStackTrace(); } + } + //Image icon) { + Object[] definition = getDefinition(component, key, "icon"); + if (set(component, (String) definition[1], icon)) { + update(component, definition[2]); + } + } + + /** + * + */ + public Image getIcon(Object component, String key) { + Object[] definition = getDefinition(component, key, "icon"); + return getIcon(component, (String) definition[1], (Image) definition[3]); + } + + /** + * + */ + public void setMethod(Object component, String key, Method method) { //java + //midp public void setMethod(Object component, String key, String method) { + Object[] definition = getDefinition(component, key, "method"); + if (set(component, (String) definition[1], method)) { + update(component, definition[2]); + } + } + + /** + * + */ + public Method getMethod(Object component, String key) { //java + //midp public String getMethod(Object component, String key) { + Object[] definition = getDefinition(component, key, "method"); + return (Method) get(component, (String) definition[1]); //java + //midp return (String) get(component, (String) definition[1]); + } + + /** + * + */ + private void update(Object component, Object mode) { + if ("parent" == mode) { + component = getParent(component); + mode = "validate"; + } + boolean firstpaint = true; + int x = 0; int y = 0; int width = 0; int height = 0; + while (component != null) { + if (!getBoolean(component, "visible", true)) { break; } + if ("paint" == mode) {//|| (firstpaint && (component == content)) + Rectangle bounds = getRectangle(component, "bounds"); + if (bounds == null) { return; } + if (firstpaint) { + x = bounds.x; y = bounds.y; + width = Math.abs(bounds.width); height = bounds.height; + firstpaint = false; + } else { + x += bounds.x; y += bounds.y; + } + if (component == content) { + repaint(x, y, width, height); + } + } + Object parent = getParent(component); + String classname = getClass(parent); + if ("combobox" == classname) { + parent = get(parent, "combolist"); + } + else if ("menu" == classname) { + parent = get(parent, "popupmenu"); + } + else if (("paint" == mode) && ("tabbedpane" == classname)) { + if (getItemImpl(parent, "component", + getInteger(parent, "selected", 0)) != component) { break; } + } + if (("layout" == mode) || (("validate" == mode) && + (("list" == classname) || ("table" == classname) || + ("tree" == classname) || ("dialog" == classname) || (parent == content)))) { + Rectangle bounds = getRectangle(parent, "bounds"); + if (bounds == null) { return; } + bounds.width = -1 * Math.abs(bounds.width); + mode = "paint"; + } + component = parent; + } + } + + // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- + + /** + * + */ + private boolean setString(Object component, + String key, String value, String defaultvalue) { + /*boolean changed = set(component, key, value); + if (changed && ("name" == key)) { + //for (Class cls = getClass(); cls != null; cls = cls.getSuperclass()) { + Field[] fields = getClass().getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + if ((fields[i].getType() == Object.class) && + fields[i].getName().equals(value)) { + try { + fields[i].set(this, component); + return true; + } catch (IllegalAccessException iae) {} + } + } + //} + try { + getClass().getField(value).set(this, component); + } catch (Exception exc) {} + } + return changed;*/ + return set(component, key, value); + } + + /** + * + */ + private String getString(Object component, + String key, String defaultvalue) { + Object value = get(component, key); + return (value == null) ? defaultvalue : (String) value; + } + + /** + * + */ + private boolean setChoice(Object component, + String key, String value, String[] values, String defaultvalue) { + if (value == null) { + return set(component, key, defaultvalue); + } + for (int i = 0; i < values.length; i++) { + if (value.equals(values[i])) { + return set(component, key, values[i]); + } + } + throw new IllegalArgumentException("unknown " + value + " for " + key); + } + + /** + * + */ + private boolean setIcon(Object component, + String key, String path, Image defaultvalue) { + return set(component, key, (path != null) ? getIcon(path) : defaultvalue); + } + + /** + * + */ + private Image getIcon(Object component, String key, Image defaultvalue) { + Object value = get(component, key); + return (value == null) ? defaultvalue : (Image) value; + } + + /** + * + */ + private boolean setBoolean(Object component, + String key, boolean value, boolean defaultvalue) { + return set(component, key, (value == defaultvalue) ? null : + (value ? Boolean.TRUE : Boolean.FALSE)); + } + + /** + * + */ + private boolean getBoolean(Object component, + String key, boolean defaultvalue) { + Object value = get(component, key); + return (value == null) ? defaultvalue : ((Boolean) value).booleanValue(); + } + + /** + * + */ + private boolean setInteger(Object component, + String key, int value, int defaultvalue) { + return set(component, key, (value == defaultvalue) ? null : new Integer(value)); + } + + /** + * + */ + private int getInteger(Object component, String key, int defaultvalue) { + Object value = get(component, key); + return (value == null) ? defaultvalue : ((Integer) value).intValue(); + } + + /** + * + */ + private void setRectangle(Object component, + String key, int x, int y, int width, int height) { + Rectangle rectangle = getRectangle(component, key); + if (rectangle != null) { + rectangle.x = x; rectangle.y = y; + rectangle.width = width; rectangle.height = height; + } + else { + set(component, key, new Rectangle(x, y, width, height)); //java + //midp set(component, key, new int[] { width, height, x, y }); + } + } + + /** + * + */ + private Rectangle getRectangle(Object component, String key) { + return (Rectangle) get(component, key); //java + //midp return (int[]) get(component, key); + } + + // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- + + /** + * + */ + public Image getIcon(String path) { + return getIcon(path, true); + } + + /** + * + */ + public Image getIcon(String path, boolean preload) { + if ((path == null) || (path.length() == 0)) { + return null; + } + Image image = null; //(Image) imagepool.get(path); + //midp try { + //midp image = Image.createImage(path); + //midp } catch (IOException ioe) {} + //java> + try { + image = Toolkit.getDefaultToolkit().getImage(getClass().getResource(path)); + //image = Toolkit.getDefaultToolkit().getImage(ClassLoader.getSystemResource(path)); + } catch (Throwable e) {} + if (image == null) { + try { + InputStream is = getClass().getResourceAsStream(path); + //InputStream is = ClassLoader.getSystemResourceAsStream(path); + if (is != null) { + byte[] data = new byte[is.available()]; + is.read(data, 0, data.length); + image = getToolkit().createImage(data); + is.close(); + } + } catch (Throwable e) {} + } + //if (image == null) { // applet code + // try { + // image = getImage(getCodeBase(), path); + // } catch (Throwable e) {} + //} + if (preload && (image != null)) { + MediaTracker mediatracker = new MediaTracker(this); + mediatracker.addImage(image, 1); + try { + mediatracker.waitForID(1, 50); + } catch (InterruptedException ie) { } + //imagepool.put(path, image); + } + // doTest(final int maxMillis) throws Exception { - @NonNls String filePath = LightAdvHighlightingTest.BASE_PATH + "/" + getTestName(false) + ".java"; - configureByFile(filePath); + configureByFile(getFilePath("")); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); getFile().getText(); //to load text @@ -92,32 +106,23 @@ public class LightAdvHighlightingPerformanceTest extends LightDaemonAnalyzerTest infos.addAll(h); } }).cpuBound().usesAllCPUCores().assertTiming(); + return DaemonAnalyzerTestCase.filter(infos, HighlightSeverity.ERROR); } - public void testaThinlet() throws Exception { + public void testAThinlet() throws Exception { List errors = doTest(Math.max(10000, 24000 - JobSchedulerImpl.CORES_COUNT * 1000)); - dump("thinlet", errors); - assertEquals(1230, errors.size()); + if (1226 != errors.size()) { + doTest(getFilePath("_hl"), false, false); + fail("Actual: " + errors.size()); + } } - private static void dump(String msg, List errors) { - if (true) return; - Collections.sort(errors, new Comparator() { - @Override - public int compare(HighlightInfo o1, HighlightInfo o2) { - return o1.toString().compareTo(o2.toString()); - } - }); - final String s = StringUtil.join(errors, StringUtil.createToStringFunction(HighlightInfo.class), "\n"); - System.out.println(msg+":\n-----------------------------------------"); - System.out.println(s); - System.out.println("\n--------------------------------------------"); - } - - public void testaClassLoader() throws Exception { + public void testAClassLoader() throws Exception { List errors = doTest(Math.max(1000, 10000 - JobSchedulerImpl.CORES_COUNT * 1000)); - dump("classloader", errors); - assertEquals(178, errors.size()); + if (173 != errors.size()) { + doTest(getFilePath("_hl"), false, false); + fail("Actual: " + errors.size()); + } } } diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java index 54cf9b45539a..8438576d3790 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java @@ -82,7 +82,7 @@ public abstract class LightDaemonAnalyzerTestCase extends LightCodeInsightTestCa protected void doTestConfiguredFile(boolean checkWarnings, boolean checkInfos, @Nullable String filePath) { getJavaFacade().setAssertOnFileLoadingFilter(VirtualFileFilter.NONE); - ExpectedHighlightingData data = new ExpectedHighlightingData(getEditor().getDocument(),checkWarnings, checkInfos); + ExpectedHighlightingData data = new ExpectedHighlightingData(getEditor().getDocument(), checkWarnings, checkInfos); checkHighlighting(data, composeLocalPath(filePath)); }