diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..6860e48
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+ Builds, tests, and runs the project PoolEdit.
+
+
+
diff --git a/images/bunny1.png b/images/bunny1.png
new file mode 100644
index 0000000..b91e354
Binary files /dev/null and b/images/bunny1.png differ
diff --git a/images/bunny2.png b/images/bunny2.png
new file mode 100644
index 0000000..c22a45a
Binary files /dev/null and b/images/bunny2.png differ
diff --git a/images/bunny3.png b/images/bunny3.png
new file mode 100644
index 0000000..d935cbe
Binary files /dev/null and b/images/bunny3.png differ
diff --git a/images/bunny4.png b/images/bunny4.png
new file mode 100644
index 0000000..fedb0a3
Binary files /dev/null and b/images/bunny4.png differ
diff --git a/images/fill.bmp b/images/fill.bmp
new file mode 100644
index 0000000..1d16388
Binary files /dev/null and b/images/fill.bmp differ
diff --git a/images/simpleBitmap.bmp b/images/simpleBitmap.bmp
new file mode 100644
index 0000000..80282b8
Binary files /dev/null and b/images/simpleBitmap.bmp differ
diff --git a/lib/idw-gpl.jar b/lib/idw-gpl.jar
new file mode 100644
index 0000000..ca49aa4
Binary files /dev/null and b/lib/idw-gpl.jar differ
diff --git a/lib/resolver.jar b/lib/resolver.jar
new file mode 100644
index 0000000..e535bdc
Binary files /dev/null and b/lib/resolver.jar differ
diff --git a/lib/serializer.jar b/lib/serializer.jar
new file mode 100644
index 0000000..de9b007
Binary files /dev/null and b/lib/serializer.jar differ
diff --git a/lib/xercesImpl.jar b/lib/xercesImpl.jar
new file mode 100644
index 0000000..e0a4c2e
Binary files /dev/null and b/lib/xercesImpl.jar differ
diff --git a/lib/xml-apis.jar b/lib/xml-apis.jar
new file mode 100644
index 0000000..d42c0ea
Binary files /dev/null and b/lib/xml-apis.jar differ
diff --git a/library.xml b/library.xml
new file mode 100644
index 0000000..69d9ea6
--- /dev/null
+++ b/library.xml
@@ -0,0 +1,1552 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/manifest.mf b/manifest.mf
new file mode 100644
index 0000000..1574df4
--- /dev/null
+++ b/manifest.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
diff --git a/manual/Parser.doc b/manual/Parser.doc
new file mode 100644
index 0000000..941dc24
Binary files /dev/null and b/manual/Parser.doc differ
diff --git a/manual/PoolEditManual.doc b/manual/PoolEditManual.doc
new file mode 100644
index 0000000..6626fc5
Binary files /dev/null and b/manual/PoolEditManual.doc differ
diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml
new file mode 100644
index 0000000..5c15c34
--- /dev/null
+++ b/nbproject/build-impl.xml
@@ -0,0 +1,1770 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set src.dir
+ Must set test.src.dir
+ Must set build.dir
+ Must set dist.dir
+ Must set build.classes.dir
+ Must set dist.javadoc.dir
+ Must set build.test.classes.dir
+ Must set build.test.results.dir
+ Must set build.classes.excludes
+ Must set dist.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No tests executed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set JVM to use for profiling in profiler.info.jvm
+ Must set profiler agent JVM arguments in profiler.info.jvmargs.agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must set fix.includes
+
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set profile.class
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+ Some tests failed; see details above.
+
+
+
+ Must select some files in the IDE or set test.class
+ Must select some method in the IDE or set test.method
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+ Must select one file in the IDE or set test.class
+
+
+
+ Must select one file in the IDE or set test.class
+ Must select some method in the IDE or set test.method
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties
new file mode 100644
index 0000000..44e33fc
--- /dev/null
+++ b/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=448a55b0
+build.xml.script.CRC32=ccf92e5c
+build.xml.stylesheet.CRC32=f85dc8f2@1.93.0.48
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=448a55b0
+nbproject/build-impl.xml.script.CRC32=c662e493
+nbproject/build-impl.xml.stylesheet.CRC32=f89f7d21@1.93.0.48
diff --git a/nbproject/project.properties b/nbproject/project.properties
new file mode 100644
index 0000000..3bc4bd8
--- /dev/null
+++ b/nbproject/project.properties
@@ -0,0 +1,95 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.processors.list=
+annotation.processing.run.all.processors=true
+application.args=
+application.title=PoolEdit
+application.vendor=hohmmat
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+debug.classpath=\
+ ${run.classpath}
+debug.modulepath=\
+ ${run.modulepath}
+debug.test.classpath=\
+ ${run.test.classpath}
+debug.test.modulepath=\
+ ${run.test.modulepath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/PoolEdit.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.idw-gpl.jar=lib/idw-gpl.jar
+file.reference.resolver.jar=lib/resolver.jar
+file.reference.serializer.jar=lib/serializer.jar
+file.reference.xercesImpl.jar=lib/xercesImpl.jar
+file.reference.xml-apis.jar=lib/xml-apis.jar
+includes=**
+jar.compress=false
+javac.classpath=\
+ ${file.reference.idw-gpl.jar}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.external.vm=false
+javac.modulepath=
+javac.processormodulepath=
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.7
+javac.target=1.7
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}:\
+ ${libs.junit.classpath}
+javac.test.modulepath=\
+ ${javac.modulepath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=
+javadoc.html5=false
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+jlink.launcher=false
+jlink.launcher.name=PoolEdit
+main.class=pooledit.Main
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=false
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}:\
+ ${file.reference.resolver.jar}:\
+ ${file.reference.serializer.jar}:\
+ ${file.reference.xercesImpl.jar}:\
+ ${file.reference.xml-apis.jar}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=-Xmx256M
+run.modulepath=\
+ ${javac.modulepath}
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+run.test.modulepath=\
+ ${javac.test.modulepath}
+src.dir=src
+test.src.dir=test
diff --git a/nbproject/project.xml b/nbproject/project.xml
new file mode 100644
index 0000000..376ac9d
--- /dev/null
+++ b/nbproject/project.xml
@@ -0,0 +1,16 @@
+
+
+ org.netbeans.modules.java.j2seproject
+
+
+ PoolEdit
+ 1.6.5
+
+
+
+
+
+
+
+
+
diff --git a/src/attributetable/AttributeTable.java b/src/attributetable/AttributeTable.java
new file mode 100644
index 0000000..04de866
--- /dev/null
+++ b/src/attributetable/AttributeTable.java
@@ -0,0 +1,821 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package attributetable;
+
+import static pooledit.Definitions.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import color.ColorPalette;
+import color.ColorIcon;
+import font.BitmapFont;
+import java.awt.Component;
+import javax.swing.DefaultCellEditor;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JComboBox;
+import javax.swing.JList;
+import javax.swing.JTable;
+import javax.swing.ListCellRenderer;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableModel;
+import pooledit.Tools;
+import pooledit.Utils;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class AttributeTable extends JTable {
+
+ private final AttributeTablePopupMenu popup = new AttributeTablePopupMenu(this);
+
+ private final TableCellEditor yesNoEditor;
+ //private final TableCellEditor trueFalseEditor;
+ private final TableCellEditor hideShowEditor;
+ private final TableCellEditor enableDisableEditor;
+ private final TableCellEditor languageEditor;
+ private final TableCellEditor horizontalJustificationEditor;
+ private final TableCellEditor nroDecimalsEditor;
+ private final TableCellEditor formatEditor;
+ private final TableCellEditor lineDirectionEditor;
+ private final TableCellEditor lineSuppressionEditor;
+ private final TableCellEditor ellipseTypeEditor;
+ private final TableCellEditor polygonTypeEditor;
+ private final TableCellEditor fontSizeEditor;
+ private final TableCellEditor fontTypeEditor;
+ private final TableCellEditor fontStyleEditor;
+ private final TableCellEditor fillTypeEditor;
+ private final TableCellEditor priorityEditor;
+ private final TableCellEditor acousticSignalEditor;
+ private final TableCellEditor colorEditor;
+ private final TableCellEditor pictureGraphicOptionEditor;
+ private final TableCellEditor meterOptionEditor;
+ private final TableCellEditor linearBbarGraphOptionEditor;
+ private final TableCellEditor archedBarGraphOptionEditor;
+ private final TableCellEditor outputStringOptionEditor;
+ private final TableCellEditor outputNumberOptionEditor;
+ private final TableCellEditor booleanAnalogEditor;
+ private final TableCellEditor validationTypeEditor;
+ private final TableCellEditor maskTypeEditor;
+
+ private final FileCellEditor fileEditor;
+ private final FileCellRenderer fileRenderer;
+ private final TableCellRenderer colorRenderer;
+
+ /**
+ * Constructor.
+ * @param model
+ */
+ public AttributeTable(TableModel model) {
+ super(model);
+ yesNoEditor = createEditor("no", "yes");
+ //trueFalseEditor = createEditor("false", "true");
+ hideShowEditor = createEditor("hide", "show");
+ enableDisableEditor = createEditor("enable", "disable");
+ languageEditor = createEditor(Locale.getISOLanguages());
+ horizontalJustificationEditor = createEditor("left", "middle", "right");
+ nroDecimalsEditor = createEditor("0", "1", "2", "3", "4", "5", "6", "7");
+ formatEditor = createEditor("fixed", "exponential");
+ lineDirectionEditor = createEditor("toplefttobottomright", "bottomlefttotopright");
+ lineSuppressionEditor = new CheckBoxListCellEditor("top", "right", "bottom", "left");
+ ellipseTypeEditor = createEditor("closed", "open", "closedsegment", "closedsection");
+ polygonTypeEditor = createEditor("convex", "nonconvex", "complex", "open");
+ fontSizeEditor = createEditor(BitmapFont.getNames());
+ fontTypeEditor = createEditor("latin1", "latin9");
+ fontStyleEditor = new CheckBoxListCellEditor("bold", "crossed", "underlined", "italic", "inverted", "flashinginverted", "flashinghidden");
+ fillTypeEditor = createEditor("nofill", "linecolour", "fillcolour", "pattern");
+ priorityEditor = createEditor("high", "medium", "low");
+ acousticSignalEditor = createEditor("high", "medium", "low", "none");
+ colorEditor = createEditor(createColorListRenderer(), ColorPalette.getAllColorNames());
+ colorRenderer = createColorRenderer();
+ pictureGraphicOptionEditor = new CheckBoxListCellEditor("transparent", "flashing");
+ meterOptionEditor = new CheckBoxListCellEditor("arc", "clockwise", "ticks", "border");
+ linearBbarGraphOptionEditor = new CheckBoxListCellEditor("border", "targetline", "ticks", "nofill", "horizontal", "growpositive");
+ archedBarGraphOptionEditor = new CheckBoxListCellEditor("border", "targetline", "nofill", "clockwise");
+ outputStringOptionEditor = new CheckBoxListCellEditor("transparent", "autowrap");
+ outputNumberOptionEditor = new CheckBoxListCellEditor("transparent", "leadingzeros", "blankzero");
+ booleanAnalogEditor = createEditor("boolean", "analog");
+ validationTypeEditor = createEditor("invalidcharacters", "validcharacters");
+ maskTypeEditor = createEditor("datamask", "alarmmask");
+
+ fileRenderer = new FileCellRenderer();
+ fileEditor = new FileCellEditor();
+
+ addMouseListener(new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent e) {
+ if (e.isPopupTrigger()) {
+ popup.showPopup(e);
+ }
+ }
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ if (e.isPopupTrigger()) {
+ popup.showPopup(e);
+ }
+ }
+ });
+ }
+
+ /*
+ * Overrides the corresponding method in the super class. Stops editing
+ * when the table is changed.
+ *
+ * NOTE: THIS WILL CAUSE INFINITE RECURSION AS STOPCELLEDITING WILL
+ * TRIGGER A NEW TABLE CHANGED EVENT!
+ *
+ public void tableChanged(TableModelEvent e) {
+ TableCellEditor tableCellEditor = getCellEditor();
+ if (tableCellEditor != null) {
+ tableCellEditor.stopCellEditing();
+ }
+ super.tableChanged(e);
+ }
+ */
+
+ private static TableCellRenderer createColorRenderer() {
+ return new DefaultTableCellRenderer() {
+ private final ColorIcon icon = new ColorIcon();
+ @Override
+ public Component getTableCellRendererComponent
+ (JTable tbl, Object val, boolean isSel,
+ boolean hasFocus, int row, int col) {
+ super.getTableCellRendererComponent
+ (tbl, val, isSel, hasFocus, row, col);
+
+ String clr = (String) val;
+ icon.setColor(ColorPalette.getColor8Bit(clr));
+ setIcon(icon);
+ setText(clr);
+ return this;
+ }
+ };
+ }
+
+ /**
+ * Creates a color list renderer.
+ * @return
+ */
+ public static ListCellRenderer createColorListRenderer() {
+ return new DefaultListCellRenderer() {
+ private final ColorIcon icon = new ColorIcon();
+ @Override
+ public Component getListCellRendererComponent
+ (JList list, Object val, int idx,
+ boolean isSel, boolean hasFocus) {
+ super.getListCellRendererComponent
+ (list, val, idx, isSel, hasFocus);
+
+ String clr = (String) val;
+ icon.setColor(ColorPalette.getColor8Bit(clr));
+ setIcon(icon);
+ setText(clr);
+ return this;
+ }
+ };
+ }
+
+ /**
+ * Gets an appropriate cell renderer.
+ * @param row
+ * @param col
+ * @return
+ */
+ @Override
+ public TableCellRenderer getCellRenderer(int row, int col) {
+ // should not happen?
+ if (col != 1) {
+ return super.getCellRenderer(row, col);
+ }
+ String attr = (String) getModel().getValueAt(row, 0);
+ if (Utils.equals(attr, BACKGROUND_COLOUR, ARC_AND_TICK_COLOUR,
+ BORDER_COLOUR, NEEDLE_COLOUR, TARGET_LINE_COLOUR,
+ COLOUR, TRANSPARENCY_COLOUR, FONT_COLOUR, // color is only for bar graphs!
+ LINE_COLOUR, FILL_COLOUR)) {
+ return colorRenderer;
+ }
+ else if (Utils.equals(attr, FILE, FILE1, FILE4, FILE8)) {
+ return fileRenderer;
+ }
+ else {
+ return super.getCellRenderer(row, col);
+ }
+ }
+
+ /**
+ * Gets an appropriate cell editor.
+ * @param row
+ * @param col
+ * @return
+ */
+ @Override
+ public TableCellEditor getCellEditor(int row, int col) {
+ // should not happen?
+ if (col != 1) {
+ return super.getCellEditor(row, col);
+ }
+ AttributeTableModel model = (AttributeTableModel) getModel();
+ String attr = (String) model.getValueAt(row, 0);
+ Element elem = model.getCurrentElement();
+ Document doc = model.getDocument(); // should be the same as elem.getOwnerDocument() ?
+
+ if (elem == null) {
+ return null;
+ }
+ String type = elem.getNodeName();
+ if (Utils.equals(attr, SELECTABLE, LATCHABLE, ENABLED, HIDDEN)) {
+ return yesNoEditor;
+ }
+ else if (Utils.equals(attr, HIDE_SHOW)) { // for the hide_show_object command
+ return hideShowEditor;
+ }
+ else if (Utils.equals(attr, ENABLE_DISABLE)) { // for the enable_disable_object command
+ return enableDisableEditor;
+ }
+ else if (Utils.equals(attr, CODE)) { // "language" is different as it can be a combination e.g. "en+de+it"
+ return languageEditor;
+ }
+ else if (Utils.equals(attr, HORIZONTAL_JUSTIFICATION)) {
+ return horizontalJustificationEditor;
+ }
+ else if (Utils.equals(attr, NUMBER_OF_DECIMALS)) {
+ return nroDecimalsEditor;
+ }
+ else if (Utils.equals(attr, FORMAT)) {
+ if (Utils.equals(type, INPUTNUMBER, OUTPUTNUMBER)) {
+ return formatEditor;
+ }
+ else if (Utils.equals(type, PICTUREGRAPHIC, FIXEDBITMAP)) {
+ // FIXME: do something else...
+ return super.getCellEditor(row, col);
+ }
+ else {
+ return super.getCellEditor(row, col);
+ }
+ }
+ else if (Utils.equals(attr, OPTIONS)) {
+ if (Utils.equals(type, PICTUREGRAPHIC)) {
+ return pictureGraphicOptionEditor;
+ }
+ else if (Utils.equals(type, METER)) {
+ return meterOptionEditor;
+ }
+ else if (Utils.equals(type, LINEARBARGRAPH)) {
+ return linearBbarGraphOptionEditor;
+ }
+ else if (Utils.equals(type, ARCHEDBARGRAPH)) {
+ return archedBarGraphOptionEditor;
+ }
+ else if (Utils.equals(type, OUTPUTSTRING)) {
+ return outputStringOptionEditor;
+ }
+ else if (Utils.equals(type, OUTPUTNUMBER, INPUTNUMBER)) {
+ return outputNumberOptionEditor;
+ }
+ else {
+ return super.getCellEditor(row, col);
+ }
+ }
+ else if (Utils.equals(attr, LINE_DIRECTION)) {
+ return lineDirectionEditor;
+ }
+ else if (Utils.equals(attr, LINE_SUPPRESSION)) {
+ return lineSuppressionEditor;
+ }
+ else if (Utils.equals(attr, ELLIPSE_TYPE)) {
+ return ellipseTypeEditor;
+ }
+ else if (Utils.equals(attr, POLYGON_TYPE)) {
+ return polygonTypeEditor;
+ }
+ else if (Utils.equals(attr, FONT_SIZE)) {
+ return fontSizeEditor;
+ }
+ else if (Utils.equals(attr, FONT_TYPE)) {
+ return fontTypeEditor;
+ }
+ else if (Utils.equals(attr, FONT_STYLE)) {
+ return fontStyleEditor;
+ }
+ else if (Utils.equals(attr, FILL_TYPE)) {
+ return fillTypeEditor;
+ }
+ else if (Utils.equals(attr, PRIORITY)) {
+ return priorityEditor;
+ }
+ else if (Utils.equals(attr, ACOUSTIC_SIGNAL)) {
+ return acousticSignalEditor;
+ }
+ else if (Utils.equals(attr, BACKGROUND_COLOUR, ARC_AND_TICK_COLOUR,
+ BORDER_COLOUR, NEEDLE_COLOUR, TARGET_LINE_COLOUR,
+ COLOUR, TRANSPARENCY_COLOUR, FONT_COLOUR,
+ LINE_COLOUR, FILL_COLOUR)) {
+ return colorEditor;
+ }
+ else if (Utils.equals(attr, FUNCTION_TYPE)) {
+ return booleanAnalogEditor;
+ }
+ else if (Utils.equals(attr, VALIDATION_TYPE)) {
+ return validationTypeEditor;
+ }
+ else if (Utils.equals(attr, MASK_TYPE)) {
+ return maskTypeEditor;
+ }
+ else if (Utils.equals(attr, ROLE)) {
+ return createEditor(findPossibleRoles(model.getCurrentNode()));
+ }
+ // NOTE: font_attributes is a name of an attribute, fontattributes
+ // is a name of an ISOBUS object
+ else if(Utils.equals(attr, FONT_ATTRIBUTES, BLOCK_FONT) ||
+ (Utils.equals(type, INPUTBOOLEAN) && Utils.equals(attr, FOREGROUND_COLOUR))) {
+ return createEditor(findElements(doc, false, FONTATTRIBUTES));
+ }
+ else if (Utils.equals(attr, VARIABLE_REFERENCE) || Utils.equals(attr, TARGET_VALUE_VARIABLE_REFERENCE)) {
+ if (Utils.equals(type, OUTPUTSTRING, INPUTSTRING)) {
+ return createEditor(findElements(doc, true, STRINGVARIABLE));
+ }
+ else if (Utils.equals(type, INPUTBOOLEAN, INPUTNUMBER, INPUTLIST, OUTPUTNUMBER, METER, LINEARBARGRAPH, ARCHEDBARGRAPH)) {
+ return createEditor(findElements(doc, true, NUMBERVARIABLE));
+ }
+ else {
+ return super.getCellEditor(row, col);
+ }
+ }
+ else if(Utils.equals(attr, ACTIVE_MASK)) {
+ return createEditor(findElements(doc, true, DATAMASK, ALARMMASK));
+ }
+ else if(Utils.equals(attr, SOFT_KEY_MASK)) {
+ return createEditor(findElements(doc, true, SOFTKEYMASK));
+ }
+ else if(Utils.equals(attr, LINE_ATTRIBUTES)) {
+ return createEditor(findElements(doc, true, LINEATTRIBUTES));
+ }
+ else if(Utils.equals(attr, FILL_ATTRIBUTES)) {
+ return createEditor(findElements(doc, true, FILLATTRIBUTES));
+ }
+ else if(Utils.equals(attr, FILL_PATTERN)) {
+ return createEditor(findElements(doc, true, PICTUREGRAPHIC));
+ }
+ else if(Utils.equals(attr, FILE, FILE1, FILE4, FILE8)){
+ return fileEditor;
+ }
+ else if(Utils.equals(attr, "parents")){
+ return createEditor(findLinkingElements(elem));
+ }
+ else {
+ return super.getCellEditor(row, col);
+ }
+ }
+
+ /**
+ * Finds all elements that are given type, and returns their names.
+ * @param doc
+ * @param empty
+ * @param names
+ * @return
+ */
+ private static Object[] findElements(Document doc, boolean empty, String ... names){
+ List namelist = new ArrayList();
+ if (empty) {
+ namelist.add(""); // empty string
+ }
+ for (int j = 0, m = names.length; j < m; j++) {
+ NodeList nodes = doc.getElementsByTagName(names[j]);
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ namelist.add(((Element) (nodes.item(i))).getAttribute(NAME));
+ }
+ }
+ return namelist.toArray();
+ }
+
+ /**
+ * Finds every element that has a include_object reference or a
+ * attribute-reference to current object. Returns an array of names
+ * of elements having a link to the specified element.
+ * @param elem
+ * @return
+ */
+ private static Object[] findLinkingElements(Element elem) {
+ List list = new ArrayList();
+ Tools.findParentElements(elem, list);
+
+ List namelist = new ArrayList();
+ namelist.add(""); // empty string
+ for (Element e : list) {
+ namelist.add(Tools.getPath(e));
+ }
+ return namelist.toArray();
+ }
+ /*
+ private static Object[] findLinkingElements(Element elem) {
+ String name = elem.getAttribute(NAME);
+ List namelist = new ArrayList();
+ namelist.add(""); // empty string
+
+ // this object can also be embedded in another object (pseudo objects,
+ // and real objects in non-linear documents)
+ Node parent = elem.getParentNode();
+ if (parent.getNodeType() == Element.ELEMENT_NODE &&
+ equals(parent.getNodeName(), OBJECTS)) {
+
+ namelist.add(((Element) parent).getAttribute(NAME));
+ }
+
+ // if the current element has no name, there is nothing more to do
+ if (name.isEmpty()) {
+ return namelist.toArray();
+ }
+
+ // iterate over all elements in the document
+ Document doc = elem.getOwnerDocument();
+ NodeList nodes = doc.getElementsByTagName("*");
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Element element = (Element) nodes.item(i);
+
+ // other objects can use include objects to make references
+ if (element.getNodeName().equals(INCLUDE_OBJECT)) {
+ if (element.getAttribute(NAME).equals(name)) {
+ parent = element.getParentNode();
+ if (parent.getNodeType() == Element.ELEMENT_NODE) {
+
+ namelist.add(((Element) parent).getAttribute(NAME));
+ }
+ }
+ }
+ // other objects can use some attributes to make references
+ else if (Utils.equalsAttribute(name, element, ACTIVE_MASK, SOFT_KEY_MASK,
+ FONT_ATTRIBUTES, BLOCK_FONT, LINE_ATTRIBUTES,
+ FILL_ATTRIBUTES, FILL_PATTERN, VARIABLE_REFERENCE,
+ TARGET_VALUE_VARIABLE_REFERENCE)) {
+
+ namelist.add(element.getAttribute(NAME));
+ }
+ }
+ return namelist.toArray();
+ }
+ */
+
+ /**
+ * This method creates a new DefaultCellEditor with JComboBox filled with
+ * the specified names. This method is divided into two parts because it
+ * is a really bad idea to say: foo(Object ... bar)
+ * @param names
+ * @return
+ */
+ static private TableCellEditor createEditor(String ... names) {
+ return createEditor((Object[]) names);
+ }
+ static private TableCellEditor createEditor(Object[] names) {
+ JComboBox cb = new JComboBox();
+ for (int i = 0, n = names.length; i < n; i++) {
+ cb.addItem(names[i]);
+ }
+ return new DefaultCellEditor(cb);
+ }
+
+ /**
+ * This method is divided into two parts because it
+ * is a really bad idea to say: foo(Object ... bar)
+ * @param renderer
+ * @param names
+ * @return
+ */
+ static private TableCellEditor createEditor(ListCellRenderer renderer, String ... names) {
+ return createEditor(renderer, (Object[]) names);
+ }
+ static private TableCellEditor createEditor(ListCellRenderer renderer, Object[] names) {
+ JComboBox cb = new JComboBox();
+ for (int i = 0, n = names.length; i < n; i++) {
+ cb.addItem(names[i]);
+ }
+ cb.setRenderer(renderer);
+ return new DefaultCellEditor(cb);
+ }
+
+ /**
+ * Checks, if at least one value equals name.
+ static private boolean equals(String name, String ... values) {
+ for (int i = 0, n = values.length; i < n; i++) {
+ if (name.equals(values[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ */
+
+ /**
+ * Check, if at least one attribute of the specified element has the given value.
+ static private boolean equalsAttribute(String value, Element element, String ... attributes) {
+ for (int i = 0, n = attributes.length; i < n; i++) {
+ if (value.equals(element.getAttribute(attributes[i]))) {
+ return true;
+ }
+ }
+ return false;
+ }
+ */
+
+ /**
+ * Finds possible roles for the specified node. If no roles are possible,
+ * this method returns a single, empty string.
+ * @param node
+ * @return
+ */
+ private static String[] findPossibleRoles(XMLTreeNode node) {
+ Node parentNode = null;
+ if (node.link() != null) {
+ parentNode = node.link().getParentNode();
+ }
+ else if (node.actual() != null) {
+ parentNode = node.actual().getParentNode();
+ }
+
+ if (parentNode == null ||
+ parentNode.getNodeType() != Node.ELEMENT_NODE) {
+ return new String[] {""};
+ }
+ Element parent = (Element) parentNode;
+ return findPossibleRoles(parent.getNodeName(), node.getType());
+ }
+
+ /**
+ * Finds possible roles for the given element type in the context of
+ * the specified parent type. If no roles are possible, this method
+ * returns a single, empty string.
+ * @param parentType
+ * @param elemType
+ * @return
+ */
+ public static String[] findPossibleRoles(String parentType, String elemType) {
+ if (Utils.equals(parentType, WORKINGSET) &&
+ Utils.equals(elemType, DATAMASK, ALARMMASK)) {
+ return new String[] {ACTIVE_MASK};
+ }
+ else if (Utils.equals(parentType, DATAMASK, ALARMMASK) &&
+ Utils.equals(elemType, SOFTKEYMASK)) {
+ return new String[] {SOFT_KEY_MASK};
+ }
+ else if (Utils.equals(parentType, INPUTSTRING, INPUTNUMBER, OUTPUTSTRING, OUTPUTNUMBER) &&
+ Utils.equals(elemType, FONTATTRIBUTES)) {
+ return new String[] {FONT_ATTRIBUTES};
+ }
+ else if (Utils.equals(parentType, INPUTBOOLEAN) &&
+ Utils.equals(elemType, FONTATTRIBUTES)) {
+ return new String[] {FOREGROUND_COLOUR};
+ }
+ else if (Utils.equals(parentType, INPUTSTRING) &&
+ Utils.equals(elemType, INPUTATTRIBUTES)) {
+ return new String[] {INPUT_ATTRIBUTES};
+ }
+ else if (Utils.equals(parentType, INPUTNUMBER, INPUTLIST, OUTPUTNUMBER, METER) &&
+ Utils.equals(elemType, NUMBERVARIABLE)) {
+ return new String[] {VARIABLE_REFERENCE};
+ }
+ else if (Utils.equals(parentType, INPUTSTRING, OUTPUTSTRING) &&
+ Utils.equals(elemType, STRINGVARIABLE)) {
+ return new String[] {VARIABLE_REFERENCE};
+ }
+ else if (Utils.equals(parentType, LINEARBARGRAPH, ARCHEDBARGRAPH) &&
+ Utils.equals(elemType, NUMBERVARIABLE)) {
+ return new String[] {VARIABLE_REFERENCE, TARGET_VALUE_VARIABLE_REFERENCE};
+ }
+ else if (Utils.equals(parentType, LINE, RECTANGLE, ELLIPSE, POLYGON) &&
+ Utils.equals(elemType, LINEATTRIBUTES)) {
+ return new String[] {LINE_ATTRIBUTES};
+ }
+ else if (Utils.equals(parentType, RECTANGLE, ELLIPSE, POLYGON) &&
+ Utils.equals(elemType, FILLATTRIBUTES)) {
+ return new String[] {FILL_ATTRIBUTES};
+ }
+ else if (Utils.equals(parentType, FILLATTRIBUTES) &&
+ Utils.equals(elemType, PICTUREGRAPHIC)) {
+
+ return new String[] {FILL_PATTERN};
+ }
+ // commands
+ else if (Utils.equals(parentType, COMMAND_HIDE_SHOW_OBJECT) &&
+ Utils.equals(elemType, CONTAINER)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_ENABLE_DISABLE_OBJECT) &&
+ Utils.equals(elemType, INPUTBOOLEAN, INPUTSTRING, INPUTNUMBER, INPUTLIST)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_SELECT_INPUT_OBJECT) &&
+ Utils.equals(elemType, INPUTBOOLEAN, INPUTSTRING, INPUTNUMBER, INPUTLIST)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CONTROL_AUDIO_DEVICE, COMMAND_SET_AUDIO_VOLUME)) {
+
+ return new String[] {""};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_CHILD_LOCATION, COMMAND_CHANGE_CHILD_POSITION)) {
+
+ return new String[] {"parent_id", "child_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_SIZE)) { // FIXME: what objects can be included here?
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_BACKGROUND_COLOUR)) { // FIXME: what objects can be included here?
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_NUMERIC_VALUE) &&
+ Utils.equals(elemType, INPUTNUMBER, OUTPUTNUMBER, METER,
+ LINEARBARGRAPH, ARCHEDBARGRAPH, NUMBERVARIABLE)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_STRING_VALUE) &&
+ Utils.equals(elemType, INPUTSTRING, OUTPUTSTRING, STRINGVARIABLE)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_END_POINT) &&
+ Utils.equals(elemType, LINE)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_FONT_ATTRIBUTES) &&
+ Utils.equals(elemType, FONTATTRIBUTES)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_LINE_ATTRIBUTES) &&
+ Utils.equals(elemType, LINEATTRIBUTES)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_FILL_ATTRIBUTES) &&
+ Utils.equals(elemType, FILLATTRIBUTES)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_ACTIVE_MASK)) {
+
+ if (Utils.equals(elemType, WORKINGSET)) {
+ return new String[] {"parent_id"};
+ }
+ else if (Utils.equals(elemType, DATAMASK, ALARMMASK)) {
+ return new String[] {"child_id"};
+ }
+ else {
+ return new String[] {""};
+ }
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_SOFT_KEY_MASK)) {
+
+ if (Utils.equals(elemType, DATAMASK, ALARMMASK)) {
+ return new String[] {"parent_id"};
+ }
+ else if (Utils.equals(elemType, SOFTKEYMASK)) {
+ return new String[] {"child_id"};
+ }
+ else {
+ return new String[] {""};
+ }
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_ATTRIBUTE)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_PRIORITY) &&
+ Utils.equals(elemType, ALARMMASK)) {
+
+ return new String[] {"object_id"};
+ }
+ else if (Utils.equals(parentType, COMMAND_CHANGE_LIST_ITEM) &&
+ Utils.equals(elemType, INPUTLIST)) {
+
+ return new String[] {"object_id"};
+ }
+ // macros
+ else if (Utils.equals(elemType, MACRO)) {
+ if (Utils.equals(parentType, WORKINGSET)) {
+ return new String[] {ON_ACTIVATE, ON_DEACTIVATE,
+ ON_CHANGE_ACTIVE_MASK, ON_CHANGE_BACKGROUND_COLOUR,
+ ON_CHANGE_CHILD_LOCATION, ON_CHANGE_CHILD_POSITION};
+ }
+ else if (Utils.equals(parentType, DATAMASK)) {
+ return new String[] {ON_SHOW, ON_HIDE,
+ ON_CHANGE_BACKGROUND_COLOUR, ON_CHANGE_CHILD_LOCATION,
+ ON_CHANGE_CHILD_POSITION, ON_CHANGE_SOFT_KEY_MASK,
+ ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, ALARMMASK)) {
+ return new String[] {ON_SHOW, ON_HIDE,
+ ON_CHANGE_BACKGROUND_COLOUR, ON_CHANGE_CHILD_LOCATION,
+ ON_CHANGE_CHILD_POSITION, ON_CHANGE_PRIORITY,
+ ON_CHANGE_SOFT_KEY_MASK, ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, CONTAINER)) {
+ return new String[] {ON_SHOW, ON_HIDE,
+ ON_CHANGE_CHILD_LOCATION, ON_CHANGE_CHILD_POSITION,
+ ON_CHANGE_SIZE};
+ }
+ else if (Utils.equals(parentType, SOFTKEYMASK)) {
+ return new String[] {ON_SHOW, ON_HIDE,
+ ON_CHANGE_BACKGROUND_COLOUR, ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, KEY)) {
+ return new String[] {ON_KEY_PRESS, ON_KEY_RELEASE,
+ ON_CHANGE_BACKGROUND_COLOUR, ON_CHANGE_CHILD_LOCATION,
+ ON_CHANGE_CHILD_POSITION, ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, BUTTON)) {
+ return new String[] {ON_KEY_PRESS, ON_KEY_RELEASE,
+ ON_CHANGE_BACKGROUND_COLOUR, ON_CHANGE_SIZE,
+ ON_CHANGE_CHILD_LOCATION, ON_CHANGE_CHILD_POSITION,
+ ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, INPUTBOOLEAN, INPUTSTRING,
+ INPUTNUMBER, INPUTLIST)) {
+ return new String[] {ON_ENABLE, ON_DISABLE,
+ ON_INPUT_FIELD_SELECTION, ON_INPUT_FIELD_DESELECTION,
+ ON_ESC, ON_CHANGE_BACKGROUND_COLOUR,
+ ON_CHANGE_VALUE, // applies to both numeric and string values
+ ON_ENTRY_OF_VALUE, ON_ENTRY_OF_NEW_VALUE,
+ ON_CHANGE_ATTRIBUTE, ON_CHANGE_SIZE};
+ }
+ else if (Utils.equals(parentType, INPUTLIST)) {
+ return new String[] {ON_ENABLE, ON_DISABLE,
+ ON_INPUT_FIELD_SELECTION, ON_INPUT_FIELD_DESELECTION,
+ ON_ESC, ON_CHANGE_VALUE, // applies to both numeric and string values
+ ON_ENTRY_OF_VALUE, ON_ENTRY_OF_NEW_VALUE,
+ ON_CHANGE_ATTRIBUTE, ON_CHANGE_SIZE};
+ }
+ else if (Utils.equals(parentType, OUTPUTSTRING, OUTPUTNUMBER)) {
+ return new String[] {ON_CHANGE_BACKGROUND_COLOUR,
+ ON_CHANGE_VALUE, ON_CHANGE_ATTRIBUTE, ON_CHANGE_SIZE};
+ }
+ else if (Utils.equals(parentType, LINE)) {
+ return new String[] {ON_CHANGE_END_POINT, ON_CHANGE_ATTRIBUTE,
+ ON_CHANGE_SIZE};
+ }
+ else if (Utils.equals(parentType, RECTANGLE, ELLIPSE, POLYGON)) {
+ return new String[] {ON_CHANGE_SIZE, ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, METER, LINEARBARGRAPH, ARCHEDBARGRAPH)) {
+ return new String[] {ON_CHANGE_VALUE, ON_CHANGE_ATTRIBUTE, ON_CHANGE_SIZE};
+ }
+ else if (Utils.equals(parentType, PICTUREGRAPHIC)) {
+ return new String[] {ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, NUMBERVARIABLE, STRINGVARIABLE)) {
+ return new String[] {ON_CHANGE_VALUE};
+ }
+ else if (Utils.equals(parentType, FONTATTRIBUTES)) {
+ return new String[] {ON_CHANGE_FONT_ATTRIBUTES, ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, LINEATTRIBUTES)) {
+ return new String[] {ON_CHANGE_LINE_ATTRIBUTES, ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, FILLATTRIBUTES)) {
+ return new String[] {ON_CHANGE_FILL_ATTRIBUTES, ON_CHANGE_ATTRIBUTE};
+ }
+ else if (Utils.equals(parentType, INPUTATTRIBUTES, OBJECTPOINTER)) {
+ return new String[] {ON_CHANGE_VALUE};
+ }
+ else {
+ return new String[] {""};
+ }
+ }
+ else {
+ return new String[] {""};
+ }
+ }
+}
diff --git a/src/attributetable/AttributeTableModel.java b/src/attributetable/AttributeTableModel.java
new file mode 100644
index 0000000..1b999c2
--- /dev/null
+++ b/src/attributetable/AttributeTableModel.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package attributetable;
+
+import java.util.Vector;
+import javax.swing.JTable;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableCellEditor;
+import javax.swing.tree.TreePath;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+import org.w3c.dom.events.MutationEvent;
+import pooledit.Utils;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class AttributeTableModel extends AbstractTableModel implements EventListener {
+
+ private Document doc;
+ private TreePath path;
+ private boolean newDoc = true;
+
+ private XMLTreeNode node;
+ private NamedNodeMap attribs;
+ private NamedNodeMap linkAttribs;
+
+ private final String[] COL_NAMES = {"Name", "Value"};
+
+ /**
+ * Sets the document.
+ * @param doc
+ */
+ public void setDocument(Document doc) {
+ if (this.doc == doc) {
+ return;
+ }
+ stopCellEditing(); // this has to be done first!
+
+ if (this.doc != null) {
+ ((EventTarget) this.doc).removeEventListener("DOMAttrModified", this, false);
+ }
+ ((EventTarget) doc).addEventListener("DOMAttrModified", this, false);
+
+ this.doc = doc;
+ this.newDoc = true;
+ this.path = null;
+ this.node = null;
+ this.attribs = null;
+ this.linkAttribs = null;
+ fireTableDataChanged();
+ }
+
+ /**
+ * Sets the active path.
+ * @param path
+ */
+ public void setActivePath(TreePath path) {
+ if (Utils.equalObjects(this.path, path) && !newDoc) {
+ return;
+ }
+ this.path = path;
+ this.newDoc = false;
+ stopCellEditing(); // this has to be done first!
+
+ if (path == null) {
+ this.attribs = null;
+ this.linkAttribs = null;
+ }
+ else {
+ this.node = (XMLTreeNode) path.getLastPathComponent();
+ this.attribs = node.actual() == null ? null : node.actual().getAttributes();
+ this.linkAttribs = node.link() == null ? null : node.link().getAttributes();
+ }
+ fireTableDataChanged();
+ }
+
+ /**
+ * Iterates over table model listeners. If an instance of JTable is found
+ * and it has an active cell editor, the editing process is interrupted.
+ *
+ * NOTE: THIS HAS TO BE TRIGGERED WHEN THE DOCUMENT OR THE ACTIVE PATH
+ * IS CHANGED - NOT WHEN THE MODEL IS CHANGED AS CALLING STOPCELLEDITING
+ * WILL CHANGE THE MODEL (INFINITE RECURSION)
+ */
+ private void stopCellEditing() {
+ // EventListenerList works really strangely, see the documentation!
+ Object[] lsts = listenerList.getListenerList();
+ // Process the listeners in reverse order
+ for (int i = lsts.length - 2; i >= 0; i -= 2) {
+ if (lsts[i+1] instanceof JTable) {
+ JTable table = (JTable) lsts[i+1];
+ TableCellEditor tableCellEditor = table.getCellEditor();
+ if (tableCellEditor != null) {
+ table.getCellEditor().stopCellEditing();
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the document.
+ * @return
+ */
+ public Document getDocument() {
+ return doc;
+ }
+
+ /**
+ * Gets the current element.
+ * @return
+ */
+ public Element getCurrentElement() {
+ return node != null ? node.actual() : null;
+ }
+
+ /**
+ * Gets the current node.
+ * @return
+ */
+ public XMLTreeNode getCurrentNode() {
+ return node;
+ }
+
+ //------------------------------------------//
+
+ @Override
+ public String getColumnName(int col) {
+ return COL_NAMES[col];
+ }
+ @Override
+ public int getColumnCount() {
+ return 2;
+ }
+ @Override
+ public int getRowCount() {
+ int count = attribs == null ? 0 : attribs.getLength() + 1; // parents;
+ count += linkAttribs == null ? 0 : linkAttribs.getLength();
+ return count;
+ }
+
+ @Override
+ public Object getValueAt(int row, int col) {
+ if (row == (getRowCount() - 1)) {
+ return col == 0 ? "parents" : "";
+ }
+ else if (row < attribs.getLength()) {
+ Attr attr = (Attr) attribs.item(row);
+ return col == 0 ? attr.getName() : attr.getValue();
+ }
+ else {
+ Attr attr = (Attr) linkAttribs.item(row - attribs.getLength());
+ return col == 0 ? attr.getName() : attr.getValue();
+ }
+ }
+
+ @Override
+ public void setValueAt(Object val, int row, int col) {
+ if (col != 1) {
+ return;
+ }
+ int nro = attribs.getLength();
+
+ // last row contains the list of nodes that reference this node
+ if (row == (getRowCount() - 1)) {
+ if (val != null && !((String) val).isEmpty()) {
+ // FIXME!
+ // if names are ambiguous, there is not enough information here
+ // to select the right node!
+ // final TreePath newpath = node.getModel().findPathByName((String) val);
+ final TreePath newpath = node.getModel().findPathByPath((String) val);
+ // System.out.println("Go to node: " + val + ", path: " + newpath);
+ fireTreeSelection(newpath);
+ }
+ }
+ else if (row < nro) {
+ Attr attr = (Attr) attribs.item(row);
+ attr.setValue((String) val);
+ fireTableCellUpdated(row, col);
+ }
+ else {
+ Attr attr = (Attr) linkAttribs.item(row - nro);
+ attr.setValue((String) val);
+ fireTableCellUpdated(row, col);
+ }
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int col) {
+ return col == 1;
+ }
+
+ //------------------------------------------------------------//
+
+ private final Vector listeners = new Vector();
+
+ /**
+ * Adds listener.
+ * @param l
+ */
+ public void addTreeSelectionListener(TreeSelectionListener l) {
+ if (!listeners.contains(l)) {
+ listeners.add(l);
+ }
+ }
+
+ /**
+ * Removes listener.
+ * @param l
+ */
+ public boolean removeTreeSelectionListener(TreeSelectionListener l) {
+ return listeners.remove(l);
+ }
+
+ /**
+ * Fires a tree selection event.
+ * @param newPath
+ */
+ public void fireTreeSelection(TreePath newPath) {
+ TreeSelectionEvent e = new TreeSelectionEvent(this,
+ newPath,
+ newPath.equals(path),
+ path,
+ newPath);
+
+ for (TreeSelectionListener l : listeners) {
+ l.valueChanged(e);
+ }
+ }
+
+ //------------------------------------------------------------//
+
+ /**
+ * Reacts to events from the dom model.
+ * Note: This code does not execute in the GUI thread!!!
+ * @param evt
+ */
+ @Override
+ public void handleEvent(Event evt) {
+ try {
+ MutationEvent mev = (MutationEvent) evt;
+ Element child = (Element) mev.getTarget();
+ String type = mev.getType();
+ if (type.equals("DOMAttrModified") && node != null) {
+
+ // we are interested in both link and actual node changes
+ if (node.link() == child || node.actual() == child) {
+ fireTableDataChanged();
+ }
+ }
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/src/attributetable/AttributeTablePopupMenu.java b/src/attributetable/AttributeTablePopupMenu.java
new file mode 100644
index 0000000..225927d
--- /dev/null
+++ b/src/attributetable/AttributeTablePopupMenu.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package attributetable;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
+import javax.swing.JTextField;
+import org.w3c.dom.Element;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class AttributeTablePopupMenu {
+
+ private final JPopupMenu popup;
+ private final AttributeTable table;
+
+ /**
+ * Creates a new instance of AttributeTablePopupMenu
+ */
+ public AttributeTablePopupMenu(AttributeTable table) {
+ this.table = table;
+ popup = createPopupMenu();
+ }
+
+ /**
+ * Creates a popup menu.
+ * @return
+ */
+ public JPopupMenu createPopupMenu() {
+ JPopupMenu pup = new JPopupMenu();
+ JMenuItem addAttribute = new JMenuItem("Add Attribute");
+ addAttribute.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ JTextField[] texts = new JTextField[] {new JTextField(),
+ new JTextField()};
+ int rv = showTextFieldDialog(null, new String[] {"Name", "Value"},
+ texts, "Add Attribute", JOptionPane.QUESTION_MESSAGE);
+ if (rv != JOptionPane.OK_OPTION) {
+ return;
+ }
+ AttributeTableModel model = (AttributeTableModel) table.getModel();
+ Element elem = model.getCurrentElement();
+ elem.setAttribute(texts[0].getText(), texts[1].getText());
+ }
+ });
+ pup.add(addAttribute);
+
+ JMenuItem removeAttribute = new JMenuItem("Remove Attribute");
+ removeAttribute.addActionListener(new ActionListener() {
+ /**
+ * Removes the specified attribute from both actual and link nodes,
+ * if any
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ JTextField[] texts = new JTextField[] {new JTextField()};
+ int rv = showTextFieldDialog(null, new String[] {"Name"},
+ texts, "Remove Attribute", JOptionPane.QUESTION_MESSAGE);
+ if (rv != JOptionPane.OK_OPTION) {
+ return;
+ }
+ AttributeTableModel model = (AttributeTableModel) table.getModel();
+ XMLTreeNode node = model.getCurrentNode();
+ if (node == null) {
+ return;
+ }
+ Element actual = node.actual();
+ if (actual != null) {
+ actual.removeAttribute(texts[0].getText());
+ }
+ Element link = node.link();
+ if (link != null) {
+ link.removeAttribute(texts[0].getText());
+ }
+ }
+ });
+ pup.add(removeAttribute);
+ return pup;
+ }
+
+ /**
+ * Show a simple dialog assembled from labels and text fields.
+ * @param parentComponent
+ * @param labels
+ * @param textFields
+ * @param title
+ * @param messageType
+ * @return
+ */
+ public int showTextFieldDialog(Component parentComponent, String[] labels,
+ JTextField[] textFields, String title, int messageType) {
+
+ Object[] objects = new Object[labels.length + textFields.length];
+ for (int i = 0, j = 0, k = 0; i < objects.length; ) {
+ if (j < labels.length) {
+ objects[i++] = labels[j++];
+ }
+ if (k < textFields.length) {
+ objects[i++] = textFields[k++];
+ }
+ }
+ return JOptionPane.showOptionDialog(parentComponent, objects, title,
+ JOptionPane.OK_CANCEL_OPTION, messageType,
+ null, null, null);
+ }
+
+ /**
+ * Shows the popup menu.
+ * @param e
+ */
+ public void showPopup(MouseEvent e) {
+ popup.show(e.getComponent(), e.getX(), e.getY());
+ }
+}
diff --git a/src/attributetable/CheckBoxListCellEditor.java b/src/attributetable/CheckBoxListCellEditor.java
new file mode 100644
index 0000000..a4b0d67
--- /dev/null
+++ b/src/attributetable/CheckBoxListCellEditor.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package attributetable;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import javax.swing.DefaultCellEditor;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.Icon;
+import javax.swing.JComboBox;
+import javax.swing.JList;
+import javax.swing.JTable;
+import javax.swing.JTree;
+import pooledit.Utils;
+
+/**
+ *
+ * @author mohman
+ */
+public class CheckBoxListCellEditor extends DefaultCellEditor {
+
+ static private final Icon CHECK = Utils.createImageIcon("/images/check.png");
+ static private final Icon NOCHECK = Utils.createImageIcon("/images/nocheck.png");
+
+ static private final Icon EMPTY = new Icon() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) { }
+ @Override
+ public int getIconWidth() { return CHECK.getIconWidth(); }
+ @Override
+ public int getIconHeight() { return CHECK.getIconHeight(); }
+ };
+
+ static class CheckBoxListCellRenderer extends DefaultListCellRenderer {
+ private final String selectedItems;
+ CheckBoxListCellRenderer(String selectedItems) {
+ this.selectedItems = selectedItems;
+ }
+ @Override
+ public Component getListCellRendererComponent(JList list, Object val,
+ int idx, boolean isSel, boolean hasFocus) {
+ super.getListCellRendererComponent(list, val, idx, isSel, hasFocus);
+
+ String clr = (String) val;
+ setIcon(clr.isEmpty() ? EMPTY : Utils.optionsContain(selectedItems, clr) ? CHECK : NOCHECK);
+ setText(clr);
+ return this;
+ }
+ }
+
+ static private JComboBox createJComboBox(String ... allItems) {
+ JComboBox cb = new JComboBox();
+ cb.addItem(""); // "no change item"
+ for (int i = 0, n = allItems.length; i < n; i++) {
+ cb.addItem(allItems[i]);
+ }
+ return cb;
+ }
+
+ private final String[] allItems;
+ private String selectedItems;
+
+ /**
+ * Constructor.
+ * @param allItems
+ */
+ public CheckBoxListCellEditor(String ... allItems) {
+ super(createJComboBox(allItems));
+ this.allItems = allItems;
+ }
+
+ /**
+ * Gets a tree cell editor component.
+ * @param tree
+ * @param value
+ * @param isSelected
+ * @param expanded
+ * @param leaf
+ * @param row
+ * @return
+ */
+ @Override
+ public Component getTreeCellEditorComponent(JTree tree, Object value,
+ boolean isSelected,
+ boolean expanded,
+ boolean leaf, int row) {
+ //this.selectedItems = tree.convertValueToText(value, isSelected,
+ // expanded, leaf, row, false);
+ this.selectedItems = (String) value;
+ JComboBox cb = (JComboBox) this.getComponent();
+ cb.setRenderer(new CheckBoxListCellRenderer(selectedItems));
+ return super.getTreeCellEditorComponent(tree, "" /*value*/, isSelected, expanded, leaf, row);
+ }
+
+ /**
+ * Gets a table cell editor component.
+ * @param table
+ * @param value
+ * @param isSelected
+ * @param row
+ * @param col
+ * @return
+ */
+ @Override
+ public Component getTableCellEditorComponent(JTable table, Object value,
+ boolean isSelected,
+ int row, int col) {
+ this.selectedItems = (String) value;
+ JComboBox cb = (JComboBox) this.getComponent();
+ cb.setRenderer(new CheckBoxListCellRenderer(selectedItems));
+ return super.getTableCellEditorComponent(table, "" /*value*/, isSelected, row, col);
+ }
+
+ /**
+ * Gets cell editor value.
+ * @return
+ */
+ @Override
+ public Object getCellEditorValue() {
+ String rv = (String) super.getCellEditorValue();
+ // selecting the empty item has no effect to the selected items
+ if (rv.isEmpty()) {
+ rv = selectedItems;
+ }
+ // selecting already selected item removes it from the list
+ else if (Utils.optionsContain(selectedItems, rv)) {
+ rv = Utils.optionsRemove(selectedItems, rv);
+ }
+ // selecting non-selected item adds it to the list
+ else {
+ rv = selectedItems + "+" + rv;
+ }
+ // regenerate options string from the "all items" list,
+ // this way the order of the items stays the same every time
+ StringBuffer out = new StringBuffer();
+ for (int i = 0, n = allItems.length; i < n; i++) {
+ if (Utils.optionsContain(rv, allItems[i])) {
+ if (out.length() > 0) {
+ out.append('+');
+ }
+ out.append(allItems[i]);
+ }
+ }
+ return out.toString();
+ }
+}
diff --git a/src/attributetable/FileCellEditor.java b/src/attributetable/FileCellEditor.java
new file mode 100644
index 0000000..c64af03
--- /dev/null
+++ b/src/attributetable/FileCellEditor.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package attributetable;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import javax.swing.DefaultCellEditor;
+import javax.swing.JButton;
+import javax.swing.JFileChooser;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.table.TableCellEditor;
+
+/**
+ *
+ * @author mohman
+ */
+public class FileCellEditor extends DefaultCellEditor implements
+ TableCellEditor, ActionListener {
+
+ static private final String PATH = "images\\";
+ private final JPanel panel = new JPanel();
+ private final JButton button = new JButton("...");
+ private final JFileChooser filechooser = new JFileChooser();
+
+ private File file;
+ private String value; // super class has probably value as well, but it is not writable?
+
+ /**
+ * Creates a new instance of FileCellEditor
+ */
+ public FileCellEditor() {
+ super(new JTextField());
+ //panel.setBackground(Color.WHITE);
+ button.setMargin(new Insets(0, 0, 0, 0));
+ button.addActionListener(this);
+
+ panel.setLayout(new BorderLayout());
+ panel.add(super.getComponent(), BorderLayout.CENTER);
+ panel.add(button, BorderLayout.EAST);
+
+ filechooser.setCurrentDirectory(new File(PATH));
+ }
+
+ /**
+ * Shows the open file dialog when the button is pressed.
+ * @param e
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (filechooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
+ file = filechooser.getSelectedFile();
+ }
+ stopCellEditing();
+ }
+
+ /**
+ * Gets a table cell editor component.
+ * @param table
+ * @param value
+ * @param isSelected
+ * @param row
+ * @param column
+ * @return
+ */
+ @Override
+ public Component getTableCellEditorComponent(JTable table, Object value,
+ boolean isSelected, int row, int column) {
+ super.getTableCellEditorComponent(table, value, isSelected, row, column);
+ this.value = (String) value;
+ filechooser.setSelectedFile(new File(this.value));
+ return panel;
+ }
+
+ /**
+ * Gets cell editor value.
+ * @return
+ */
+ @Override
+ public Object getCellEditorValue() {
+ if (file != null) {
+ value = file.getName();
+ file = null;
+ }
+ else {
+ value = (String) super.getCellEditorValue();
+ }
+ return value;
+ }
+}
diff --git a/src/attributetable/FileCellRenderer.java b/src/attributetable/FileCellRenderer.java
new file mode 100644
index 0000000..3108855
--- /dev/null
+++ b/src/attributetable/FileCellRenderer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package attributetable;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Insets;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableCellRenderer;
+
+/**
+ *
+ * @author mohman
+ */
+public class FileCellRenderer extends DefaultTableCellRenderer {
+
+ private final JPanel panel = new JPanel();
+ /** This button is just for show, it does not have any listeners */
+ private final JButton button = new JButton("...");
+
+ /**
+ * Creates a new instance of FileCellEditor
+ */
+ public FileCellRenderer() {
+ button.setMargin(new Insets(0, 0, 0, 0));
+ panel.setLayout(new BorderLayout());
+ panel.add(this);
+ panel.add(button, BorderLayout.EAST);
+ }
+
+ /**
+ * DefaultTableCellRenderer extends JLabel and normally the returned
+ * component from this method is "this". Both JPanel and JLabel extend
+ * JComponent, and so they both share foreground and background attributes.
+ * @param tbl
+ * @param val
+ * @param isSel
+ * @param hasFocus
+ * @param row
+ * @param col
+ * @return
+ */
+ @Override
+ public Component getTableCellRendererComponent(JTable tbl, Object val,
+ boolean isSel, boolean hasFocus, int row, int col) {
+ super.getTableCellRendererComponent(tbl, val, isSel, hasFocus, row, col);
+ panel.setForeground(getForeground());
+ panel.setBackground(getBackground());
+ return panel;
+ }
+}
diff --git a/src/color/ColorIcon.java b/src/color/ColorIcon.java
new file mode 100644
index 0000000..edd6ea8
--- /dev/null
+++ b/src/color/ColorIcon.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package color;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import javax.swing.Icon;
+
+/**
+ *
+ * @author mohman
+ */
+public class ColorIcon implements Icon {
+ final int SIZE = 11;
+ private Color color;
+
+ /**
+ * Constructor.
+ */
+ public ColorIcon() {
+ this(Color.BLACK);
+ }
+
+ /**
+ * Constructor.
+ * @param color
+ */
+ public ColorIcon(Color color) {
+ setColor(color);
+ }
+
+ /**
+ * Sets color.
+ * @param color
+ */
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ /**
+ * Gets icon height.
+ * @return
+ */
+ @Override
+ public int getIconHeight() {
+ return SIZE;
+ }
+
+ /**
+ * Gets icon width.
+ * @return
+ */
+ @Override
+ public int getIconWidth() {
+ return SIZE;
+ }
+
+ /**
+ * Paints the icon.
+ * @param c
+ * @param g
+ * @param x
+ * @param y
+ */
+ @Override
+ public void paintIcon(Component c,
+ Graphics g,
+ int x, int y) {
+ g.setColor(color);
+ g.fillRect(x, y, SIZE, SIZE);
+ g.setColor(Color.BLACK);
+ g.drawRect(x, y, SIZE - 1, SIZE - 1);
+ }
+}
diff --git a/src/color/ColorPalette.java b/src/color/ColorPalette.java
new file mode 100644
index 0000000..d818434
--- /dev/null
+++ b/src/color/ColorPalette.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package color;
+
+import java.awt.Color;
+
+/**
+ *
+ * @author mohman
+ */
+public class ColorPalette {
+
+ static private final String[] NAMES = {
+ "black", "white", "green", "teal",
+ "maroon", "purple", "olive", "silver",
+ "grey", "blue", "lime", "cyan",
+ "red", "magenta", "yellow", "navy"
+ };
+
+ static private final int[] VALUES_8BIT = {
+ 0x000000, 0xFFFFFF, 0x009900, 0x009999, //0
+ 0x990000, 0x990099, 0x999900, 0xCCCCCC, //4
+ 0x999999, 0x0000FF, 0x00FF00, 0x00FFFF, //8
+ 0xFF0000, 0xFF00FF, 0xFFFF00, 0x000099, //12
+ 0x000000, 0x000033, 0x000066, 0x000099, //16
+ 0x0000CC, 0x0000FF, 0x003300, 0x003333, //20
+ 0x003366, 0x003399, 0x0033CC, 0x0033FF, //24
+ 0x006600, 0x006633, 0x006666, 0x006699, //28
+ 0x0066CC, 0x0066FF, 0x009900, 0x009933, //32
+ 0x009966, 0x009999, 0x0099CC, 0x0099FF, //36
+ 0x00CC00, 0x00CC33, 0x00CC66, 0x00CC99, //40
+ 0x00CCCC, 0x00CCFF, 0x00FF00, 0x00FF33, //44
+ 0x00FF66, 0x00FF99, 0x00FFCC, 0x00FFFF, //48
+ 0x330000, 0x330033, 0x330066, 0x330099, //52
+ 0x3300CC, 0x3300FF, 0x333300, 0x333333, //56
+ 0x333366, 0x333399, 0x3333CC, 0x3333FF, //60
+ 0x336600, 0x336633, 0x336666, 0x336699, //64
+ 0x3366CC, 0x3366FF, 0x339900, 0x339933, //68
+ 0x339966, 0x339999, 0x3399CC, 0x3399FF, //72
+ 0x33CC00, 0x33CC33, 0x33CC66, 0x33CC99, //76
+ 0x33CCCC, 0x33CCFF, 0x33FF00, 0x33FF33, //80
+ 0x33FF66, 0x33FF99, 0x33FFCC, 0x33FFFF, //84
+ 0x660000, 0x660033, 0x660066, 0x660099, //88
+ 0x6600CC, 0x6600FF, 0x663300, 0x663333, //92
+ 0x663366, 0x663399, 0x6633CC, 0x6633FF, //96
+ 0x666600, 0x666633, 0x666666, 0x666699, //100
+ 0x6666CC, 0x6666FF, 0x669900, 0x669933, //104
+ 0x669966, 0x669999, 0x6699CC, 0x6699FF, //108
+ 0x66CC00, 0x66CC33, 0x66CC66, 0x66CC99, //112
+ 0x66CCCC, 0x66CCFF, 0x66FF00, 0x66FF33, //116
+ 0x66FF66, 0x66FF99, 0x66FFCC, 0x66FFFF, //120
+ 0x990000, 0x990033, 0x990066, 0x990099, //124
+ 0x9900CC, 0x9900FF, 0x993300, 0x993333, //128
+ 0x993366, 0x993399, 0x9933CC, 0x9933FF, //132
+ 0x996600, 0x996633, 0x996666, 0x996699, //136
+ 0x9966CC, 0x9966FF, 0x999900, 0x999933, //140
+ 0x999966, 0x999999, 0x9999CC, 0x9999FF, //144
+ 0x99CC00, 0x99CC33, 0x99CC66, 0x99CC99, //148
+ 0x99CCCC, 0x99CCFF, 0x99FF00, 0x99FF33,
+ 0x99FF66, 0x99FF99, 0x99FFCC, 0x99FFFF,
+ 0xCC0000, 0xCC0033, 0xCC0066, 0xCC0099,
+ 0xCC00CC, 0xCC00FF, 0xCC3300, 0xCC3333,
+ 0xCC3366, 0xCC3399, 0xCC33CC, 0xCC33FF,
+ 0xCC6600, 0xCC6633, 0xCC6666, 0xCC6699,
+ 0xCC66CC, 0xCC66FF, 0xCC9900, 0xCC9933,
+ 0xCC9966, 0xCC9999, 0xCC99CC, 0xCC99FF,
+ 0xCCCC00, 0xCCCC33, 0xCCCC66, 0xCCCC99,
+ 0xCCCCCC, 0xCCCCFF, 0xCCFF00, 0xCCFF33,
+ 0xCCFF66, 0xCCFF99, 0xCCFFCC, 0xCCFFFF,
+ 0xFF0000, 0xFF0033, 0xFF0066, 0xFF0099,
+ 0xFF00CC, 0xFF00FF, 0xFF3300, 0xFF3333,
+ 0xFF3366, 0xFF3399, 0xFF33CC, 0xFF33FF,
+ 0xFF6600, 0xFF6633, 0xFF6666, 0xFF6699,
+ 0xFF66CC, 0xFF66FF, 0xFF9900, 0xFF9933,
+ 0xFF9966, 0xFF9999, 0xFF99CC, 0xFF99FF,
+ 0xFFCC00, 0xFFCC33, 0xFFCC66, 0xFFCC99,
+ 0xFFCCCC, 0xFFCCFF, 0xFFFF00, 0xFFFF33,
+ 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFFFFFF
+ };
+ static private final int[] VALUES_4BIT = new int[VALUES_8BIT.length];
+ static private final int[] VALUES_1BIT = new int[VALUES_8BIT.length];
+
+ static private final Color[] COLORS_8BIT = new Color[VALUES_8BIT.length];
+ static private final Color[] COLORS_4BIT = new Color[VALUES_8BIT.length];
+ static private final Color[] COLORS_1BIT = new Color[VALUES_8BIT.length];
+ static private final String[] ALL_NAMES = new String[VALUES_8BIT.length];
+
+ static public final int COLOR_8BIT = 8;
+ static public final int COLOR_4BIT = 4;
+ static public final int COLOR_1BIT = 1;
+
+ static {
+ // init names
+ for (int i = 0, n = VALUES_8BIT.length; i < n; i++) {
+ ALL_NAMES[i] = getName(i);
+ }
+ // init 8-bit colors
+ for (int i = 0, n = VALUES_8BIT.length; i < n; i++) {
+ Color c = new Color(VALUES_8BIT[i]);
+ VALUES_8BIT[i] = c.getRGB(); // this may seem lika a circular definition, but it makes sure that alpha is 0xFF
+ COLORS_8BIT[i] = c;
+ }
+ // init 4-bit colors
+ for (int i = 0, n = VALUES_8BIT.length; i < n; i++) {
+ Color c = findClosestColor(COLORS_8BIT[i], COLORS_8BIT, 16);
+ VALUES_4BIT[i] = c.getRGB();
+ COLORS_4BIT[i] = c;
+ }
+ // init 1-bit colors
+ for (int i = 0, n = VALUES_8BIT.length; i < n; i++) {
+ Color c = findClosestColor(COLORS_8BIT[i], COLORS_8BIT, 2);
+ VALUES_1BIT[i] = c.getRGB();
+ COLORS_1BIT[i] = c;
+ }
+
+ /*
+ // some quick tests
+ for (int i = 0; i < 0xFFFFFF; i++) {
+ int ind_a = getPaletteIndexFast(i);
+ int a = getColorRGB(i, COLOR_8BIT);
+ int ind_b = getPaletteIndex(i);
+ int b = getColor(ind_b, COLOR_8BIT).getRGB();
+ if (a != b) {
+ //if (ind_a != ind_b) {
+ System.out.format("I: %d, A: 0x%X B: 0x%X IND_A: %d IND_B: %d NEAREST: 0x%X\n", i, a, b, ind_a, ind_b, getNearestColor(i));
+ break;
+ }
+ }
+ System.out.println("PASSED " + VALUES_8BIT.length);
+ */
+ }
+
+ /**
+ * Finds the closes color, used for color array initializations.
+ * @param color
+ * @param colorSpace
+ * @param colorDepth
+ * @return
+ */
+ static public Color findClosestColor(Color color, Color[] colorSpace, int colorDepth) {
+ int minIndex = 0;
+ int minDist = calculateDistance(colorSpace[minIndex], color);
+ for (int i = 0; i < colorDepth; i++) {
+ int dist = calculateDistance(colorSpace[i], color);
+ if (dist < minDist) {
+ minDist = dist;
+ minIndex = i;
+ }
+ }
+ return colorSpace[minIndex];
+ }
+
+ /**
+ * Returns how far given colors are
+ * FIXME: which metric is more reasonable? Use hue-space instead of rgb-space?
+ * @param c1
+ * @param c2
+ * @return
+ */
+ static private int calculateDistance(Color c1, Color c2) {
+
+ return Math.abs(c1.getRed() - c2.getRed()) +
+ Math.abs(c1.getGreen() - c2.getGreen()) +
+ Math.abs(c1.getBlue() - c2.getBlue());
+ /*
+ int dr = c1.getRed() - c2.getRed();
+ int dg = c1.getGreen() - c2.getGreen();
+ int db = c1.getBlue() - c2.getBlue();
+ return dr * dr + dg * dg + db * db;
+ */
+ }
+
+ /**
+ * Returns the nearest color in the palette - given the color depth.
+ * This method is used for object colors, but not for image data.
+ * @param index
+ * @param colorDepth
+ * @return
+ */
+ static public Color getColor(int index, int colorDepth) {
+ switch (colorDepth) {
+ case COLOR_1BIT:
+ return COLORS_1BIT[index];
+ case COLOR_4BIT:
+ return COLORS_4BIT[index];
+ case COLOR_8BIT:
+ return COLORS_8BIT[index];
+ default:
+ throw new IllegalStateException("invalid colorDepth (" + colorDepth + ")");
+ }
+ }
+
+ /**
+ * Returns the nearest color (argb) in the palette - given the color depth.
+ * Alpha is always 0xFF. This method is used for image data. It should be
+ * as fast as possible!
+ * @param rgb
+ * @param colorDepth
+ * @return
+ */
+ static public int getColorRGB(int rgb, int colorDepth) {
+ int r3 = ((((rgb >> 16) & 0xFF) + 26) / 51);
+ int g3 = ((((rgb >> 8) & 0xFF) + 26) / 51); // why 26? why not 25?
+ int b3 = ((((rgb) & 0xFF) + 26) / 51);
+ int index = 36 * r3 + 6 * g3 + b3 + 16;
+ switch (colorDepth) {
+ case COLOR_1BIT:
+ return VALUES_1BIT[index];
+ case COLOR_4BIT:
+ return VALUES_4BIT[index];
+ case COLOR_8BIT:
+ return VALUES_8BIT[index];
+ default:
+ throw new IllegalStateException("invalid colorDepth (" + colorDepth + ")");
+ }
+ }
+
+ /**
+ * Returns the specified color.
+ * @param name
+ * @param colorDepth
+ * @return
+ */
+ static public Color getColor(String name, int colorDepth) {
+ return getColor(getIndex(name), colorDepth);
+ }
+
+ /**
+ * Returns the 8-bit color.
+ * @param name
+ * @return
+ */
+ static public Color getColor8Bit(String name) {
+ return getColor(getIndex(name), COLOR_8BIT);
+ }
+
+ /**
+ * Returns the name of the specified color.
+ * @param index
+ * @return
+ */
+ static public String getName(int index) {
+ if (index < NAMES.length) {
+ return NAMES[index];
+ }
+ else {
+ return Integer.toString(index);
+ }
+ }
+
+ /**
+ * Returns the index of the specified color.
+ * @param name
+ * @return
+ */
+ static public int getIndex(String name) {
+ for (int i = 0, n = NAMES.length; i < n; i++) {
+ if (NAMES[i].equals(name)) {
+ return i;
+ }
+ }
+ try {
+ return Integer.parseInt(name);
+ }
+ catch (NumberFormatException ex) {
+ return 0;
+ }
+ }
+
+ /**
+ * Returns the normalized name, e.g. 0 -> black, 1 -> white, ...,
+ * 16 -> 16, 17 -> 17, ...
+ * @param name
+ * @return
+ */
+ static public String normalizeName(String name) {
+ return getName(getIndex(name));
+ }
+
+ /**
+ * Returns the names of the first 16 colors.
+ * @return
+ */
+ static public String[] getColorNames() {
+ return NAMES;
+ }
+
+ /**
+ * Returns the names of all colors.
+ * @return
+ */
+ static public String[] getAllColorNames() {
+ return ALL_NAMES;
+ }
+
+ /**
+ * Returns the number of colors.
+ * @return
+ */
+ static public int getNroColors() {
+ return VALUES_8BIT.length;
+ }
+
+ /**
+ * Returns the nearest color (argb) in the palette.
+ * @param rgb
+ * @return
+ */
+ static public int getNearestColor(int rgb) {
+ int r = (rgb >> 16) & 0xFF;
+ int g = (rgb >> 8) & 0xFF;
+ int b = (rgb) & 0xFF;
+ return getNearestColor(r, g, b);
+ }
+
+ /**
+ * Returns the nearest color (argb) in the palette. Alpha is always 0xFF.
+ * @param r
+ * @param g
+ * @param b
+ * @return
+ */
+ static public int getNearestColor(int r, int g, int b) {
+ r = ((r + 26) / 51) * 51;
+ g = ((g + 26) / 51) * 51; // why 26? why not 25?
+ b = ((b + 26) / 51) * 51;
+ return (0xFF << 24) + (r << 16) + (g << 8) + b;
+ }
+
+ /**
+ * Returns the index of the nearest color in the palette.
+ * @param rgb
+ * @return
+ */
+ static public int getPaletteIndex(int rgb) {
+ int r = (rgb >> 16) & 0xFF;
+ int g = (rgb >> 8) & 0xFF;
+ int b = (rgb) & 0xFF;
+ return getPaletteIndex(r, g, b);
+ }
+
+ /**
+ * Returns the index of the nearest color in the palette.
+ * @param r
+ * @param g
+ * @param b
+ * @return
+ */
+ static public int getPaletteIndex(int r, int g, int b){
+ int color = getNearestColor(r, g, b);
+ for (int i = 0; i < VALUES_8BIT.length; i++) {
+ if (color == VALUES_8BIT[i]) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the index of the nearest color in the palette very fast.
+ * Indices 0..15 are never returned (as they are repeated later in the
+ * palette).
+ * @param rgb
+ * @return
+ */
+ static public int getPaletteIndexFast(int rgb) {
+ int r = (rgb >> 16) & 0xFF;
+ int g = (rgb >> 8) & 0xFF;
+ int b = rgb & 0xFF;
+ return getPaletteIndexFast(r, g, b);
+ }
+
+ /**
+ * Returns the index of the nearest color in the palette very fast.
+ * Indices 0..15 are never returned (as they are repeated later in the
+ * palette).
+ * @param r
+ * @param g
+ * @param b
+ * @return
+ */
+ static public int getPaletteIndexFast(int r, int g, int b) {
+ int r3 = (r + 26) / 51;
+ int g3 = (g + 26) / 51; // why 26? why not 25?
+ int b3 = (b + 26) / 51;
+ return 36 * r3 + 6 * g3 + b3 + 16;
+ }
+}
diff --git a/src/dragndrop/XMLTransferable.java b/src/dragndrop/XMLTransferable.java
new file mode 100644
index 0000000..a9ec908
--- /dev/null
+++ b/src/dragndrop/XMLTransferable.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package dragndrop;
+
+import static pooledit.Definitions.*;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import javax.swing.tree.TreePath;
+import pooledit.Tools;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class XMLTransferable implements Transferable {
+
+ private TreePath[] paths;
+
+ /** Creates a new instance of XMLTransferrable */
+ public XMLTransferable(TreePath[] paths) {
+ this.paths = paths;
+ }
+
+ public TreePath[] getPaths() {
+ return paths;
+ }
+
+ /**
+ * Returns an array of DataFlavor objects indicating the flavors the data
+ * can be provided in.
+ */
+ @Override
+ public DataFlavor[] getTransferDataFlavors() {
+ return new DataFlavor[] {DataFlavor.stringFlavor};
+ }
+
+ /**
+ * Returns whether or not the specified data flavor is supported for
+ * this object.
+ */
+ @Override
+ public boolean isDataFlavorSupported(DataFlavor flavor) {
+ return DataFlavor.stringFlavor.equals(flavor);
+ }
+
+ /**
+ * Returns an object which represents the data to be transferred. The class
+ * of the object returned is defined by the representation class of the flavor.
+ */
+ @Override
+ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
+
+ if (DataFlavor.stringFlavor.equals(flavor)) {
+ String s = "";
+
+ // FIXME: do not send separate objects, combine them?
+ for (int i = 0, n = paths.length; i < n; i++) {
+ XMLTreeNode node = (XMLTreeNode) paths[i].getLastPathComponent();
+
+ // broken link
+ if (node.isType(INCLUDE_OBJECT)) {
+ s = Tools.writeToStringNoDec(node.actual());
+ }
+ else {
+ //s = Tools.writeToStringNoDec(Tools.createMergedElementRecursive(node.actual(), node.getModel().getNameMap()));
+
+ // this is very intresting:
+ // the createMergedElementRecursive method is very good at handling links
+ // - if the first object is a link, the method must also be given a link,
+ // if a consistent behaviour is expected - it usually is :)
+ s = Tools.writeToStringNoDec(Tools.createMergedElementRecursive(
+ node.link() != null ? node.link() : node.actual(),
+ node.getModel().getNameMap()));
+ }
+ }
+ return s;
+ }
+ throw new UnsupportedFlavorException(flavor);
+ }
+}
diff --git a/src/font/BitmapFont.java b/src/font/BitmapFont.java
new file mode 100644
index 0000000..9526df8
--- /dev/null
+++ b/src/font/BitmapFont.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package font;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ByteLookupTable;
+import java.awt.image.LookupOp;
+import java.awt.image.LookupTable;
+import java.io.IOException;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.Scanner;
+import javax.imageio.ImageIO;
+import java.net.URL;
+
+/**
+ *
+ * @author mohman
+ */
+public class BitmapFont {
+
+ static public final int JUSTIFICATION_LEFT = 0;
+ static public final int JUSTIFICATION_MIDDLE = 1;
+ static public final int JUSTIFICATION_RIGHT = 2;
+
+ static private final String[] NAMES = {
+ "6x8", "8x8", "8x12",
+ "12x16", "16x16", "16x24",
+ "24x32", "32x32", "32x48",
+ "48x64", "64x64", "64x96",
+ "96x128", "128x128", "128x192"
+ };
+
+ /*
+ static private final String[] TYPES = {
+ "latin1", "latin9"
+ };
+ */
+
+ static private final String[] STYLES = {
+ "Bold", "Crossed Out", "Underlined", "Italic",
+ "Inverted", "Flashing Inverted", "Flashing Background"
+ };
+
+ static private final Dimension[] DIMENSIONS =
+ new Dimension[NAMES.length];
+
+ static private final BufferedImage[] BITMAPS =
+ new BufferedImage[NAMES.length];
+
+ static private final String PATH = "/images/";
+
+ static private final String[] FILENAMES = {
+ "font6x8.png", "font8x8_.png", "font8x12_.png",
+ "font12x16_.png", "font16x16_.png", "font16x24_.png",
+ "font24x32_.png", "font32x32_.png", "font32x48_.png",
+ "font48x64_.png", "font64x64_.png", "font64x96_.png",
+ "font96x128_.png", "font128x128_.png", "font128x192_.png"
+ };
+
+ /** Needed for changing font color */
+ static private byte[][] lookupdata = new byte[4][256];
+ static private final LookupTable ltab = new ByteLookupTable(0, lookupdata);
+ static private final LookupOp lop = new LookupOp(ltab, null);
+
+ static {
+ // create dimensions
+ for (int i = 0, n = NAMES.length; i < n; i++) {
+ Scanner s = new Scanner(NAMES[i]).useDelimiter("x");
+ DIMENSIONS[i] = new Dimension(s.nextInt(), s.nextInt());
+ }
+ // use identity transform to the alpha channel
+ for (int i = 0; i < 256; i++) {
+ lookupdata[3][i] = (byte) i;
+ }
+ }
+
+ /**
+ * Gets font names (6x8, 8x8, etc).
+ * @return
+ */
+ static public String[] getNames() {
+ return NAMES;
+ }
+
+ /*
+ static public String[] getTypes() {
+ return TYPES;
+ }
+ */
+
+ /**
+ * Gets the specified bitmap.
+ * @param index
+ * @return
+ * @throws java.io.IOException
+ */
+ static public BufferedImage getBitmap(int index) throws IOException {
+ // load bitmaps lazily
+ if (BITMAPS[index] != null) {
+ return BITMAPS[index];
+ }
+ else {
+ URL url = BitmapFont.class.getResource(PATH + FILENAMES[index]);
+ if (url != null) {
+ URLConnection conn = url.openConnection();
+ BITMAPS[index] = ImageIO.read(conn.getInputStream());
+ }
+ return BITMAPS[index];
+ }
+ }
+
+ /**
+ * Maps font names to font indices.
+ * FIXME: name can't be a number
+ * @param font
+ * @return
+ */
+ static public int nameToIndex(String font) {
+ for (int i = 0, n = NAMES.length; i < n; i++) {
+ if (font.equals(NAMES[i])) {
+ return i;
+ }
+ }
+ throw new RuntimeException();
+ }
+
+ /**
+ * Maps font indices to font dimensions.
+ * @param index
+ * @return
+ */
+ static public Dimension indexToDimension(int index) {
+ return nameToDimension(indexToName(index));
+ }
+
+ /*
+ * Works, but is very slow for bigger fonts!
+ *
+ static public void changeFontColor(String font, Color fontColor) throws IOException {
+ int fontIndex = nameToIndex(font);
+ BufferedImage img = getBitmap(fontIndex);
+ for(int y = 0; y < img.getHeight(); y++)
+ for(int x = 0; x < img.getWidth(); x++) {
+ int alpha = img.getRGB(x, y) >>> 24;
+ if(alpha == 0xFF) {
+ int color = fontColor.getRGB();
+ img.setRGB(x,y, color);
+ }
+ }
+ }
+ */
+
+ /**
+ *
+ * @param font
+ * @param color
+ * @throws java.io.IOException
+ */
+ static public void changeFontColor(String font, Color color) throws IOException {
+ // map every color to the target color
+ Arrays.fill(lookupdata[0], (byte) color.getRed());
+ Arrays.fill(lookupdata[1], (byte) color.getGreen());
+ Arrays.fill(lookupdata[2], (byte) color.getBlue());
+ }
+
+ /**
+ * Maps font indices to font names.
+ * @param index
+ * @return
+ */
+ static public String indexToName(int index) {
+ return NAMES[index];
+ }
+
+ /**
+ * Maps font name to font dimension.
+ * @param font
+ * @return
+ */
+ static public Dimension nameToDimension(String font) {
+ return DIMENSIONS[nameToIndex(font)];
+ }
+
+ /**
+ * Paints character with the specified font (name).
+ * @param g
+ * @param ch
+ * @param font the name of the font
+ * @param x
+ * @param y
+ * @throws java.io.IOException
+ */
+ static public void paintChar(Graphics g, char ch,
+ String font, int x, int y) throws IOException {
+ paintChar(g, ch, nameToIndex(font), x, y);
+ }
+
+ /**
+ * Paints character with the specified font (index).
+ * @param g
+ * @param ch
+ * @param font the index of the font
+ * @param x
+ * @param y
+ * @throws java.io.IOException
+ */
+ static public void paintChar(Graphics g, char ch,
+ int font, int x, int y) throws IOException {
+ Graphics2D gfx = (Graphics2D) g;
+ Dimension d = DIMENSIONS[font];
+ int sx = d.width * (ch % 16);
+ int sy = d.height * (ch / 16);
+ BufferedImage img =
+ getBitmap(font).getSubimage(sx, sy, d.width, d.height);
+
+ gfx.drawImage(img, lop, x, y);
+ }
+
+ /**
+ * Paints text with the specified font (name).
+ * @param g
+ * @param str
+ * @param font the name of the font
+ * @param x
+ * @param y
+ * @throws java.io.IOException
+ */
+ static public void paintString(Graphics g, String str, String font,
+ int x, int y) throws IOException {
+
+ paintString(g, str, nameToIndex(font), x, y);
+ }
+
+ /**
+ * Paints text with the specified font (index).
+ * @param g
+ * @param str
+ * @param font the index of the font
+ * @param x
+ * @param y
+ * @throws java.io.IOException
+ */
+ static public void paintString(Graphics g, String str,
+ int font, int x, int y) throws IOException {
+
+ int w = DIMENSIONS[font].width;
+ for (int i = 0, n = str.length(); i < n; i++) {
+ paintChar(g, str.charAt(i), font, x + w * i, y);
+ }
+ }
+
+ /**
+ * Paints text with the specified font (name) and justification.
+ * @param g
+ * @param str
+ * @param font the name of the font
+ * @param x
+ * @param y
+ * @param width
+ * @param justification
+ * @throws java.io.IOException
+ */
+ static public void paintString(Graphics g, String str, String font,
+ int x, int y, int width, int justification) throws IOException {
+
+ paintString(g, str, nameToIndex(font), x, y, width, justification);
+ }
+
+ /**
+ * Paints text with the specified font (index) and justification.
+ * @param g
+ * @param str
+ * @param font the index of the font
+ * @param x
+ * @param y
+ * @param width
+ * @param justification
+ * @throws java.io.IOException
+ */
+ static public void paintString(Graphics g, String str, int font,
+ int x, int y, int width, int justification) throws IOException {
+
+ int offset = width - str.length() * DIMENSIONS[font].width;
+ switch (justification) {
+ case JUSTIFICATION_LEFT:
+ paintString(g, str, font, x, y);
+ break;
+ case JUSTIFICATION_MIDDLE:
+ paintString(g, str, font, x + offset / 2, y);
+ break;
+ case JUSTIFICATION_RIGHT:
+ paintString(g, str, font, x + offset, y);
+ break;
+ }
+ }
+
+ /**
+ * Wraps text to the next line after the specified width.
+ * @param g
+ * @param str
+ * @param font the name of the font
+ * @param x
+ * @param y
+ * @param width
+ * @param justification
+ * @throws java.io.IOException
+ */
+ static public void paintWrapString(Graphics g, String str, String font,
+ int x, int y, int width, int justification) throws IOException {
+
+ paintWrapString(g, str, nameToIndex(font), x, y, width, justification);
+ }
+
+ /**
+ * Wraps text to the next line after the specified width.
+ * @param g
+ * @param str
+ * @param font the index of the font
+ * @param x
+ * @param y
+ * @param width
+ * @param justification
+ * @throws java.io.IOException
+ */
+ static public void paintWrapString(Graphics g, String str, int font,
+ int x, int y, int width, int justification) throws IOException {
+
+ Dimension dim = DIMENSIONS[font];
+ int cols = width / dim.width;
+ int len;
+ int i;
+
+ while ((len = str.length()) > cols) {
+ // insert line break at the last possible white space
+ for (i = cols; i >= 0; i--) {
+ if (Character.isWhitespace(str.charAt(i))) {
+ paintString(g, str.substring(0, i), font, x, y, width, justification);
+ str = str.substring(i + 1);
+ break;
+ }
+ }
+ // force line break
+ if (i < 0) {
+ paintString(g, str.substring(0, cols), font, x, y, width, justification);
+ str = str.substring(cols);
+ }
+ y += dim.height;
+ }
+ paintString(g, str, font, x, y, width, justification);
+ }
+
+ private final String name;
+ private Color color;
+
+ /**
+ * Constructor.
+ * @param name
+ * @param color
+ */
+ public BitmapFont(String name, Color color) {
+ this.name = name;
+ this.color = color;
+ }
+
+ /**
+ * Sets font color.
+ * @param color
+ */
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ /**
+ * Gets font color.
+ * @return
+ */
+ public Color getColor() {
+ return color;
+ }
+
+ /**
+ * Gets font dimension.
+ * @return
+ */
+ public Dimension getDimension(){
+ return nameToDimension(name);
+ }
+
+ /**
+ * Paints the specified value with this font.
+ * @param g
+ * @param value
+ * @param x
+ * @param y
+ * @param width
+ * @param justification
+ * @throws java.io.IOException
+ */
+ public void paint(Graphics g, String value,
+ int x, int y, int width, int justification) throws IOException {
+
+ changeFontColor(name, color);
+ paintString(g, value, name, x, y, width, justification);
+ }
+
+ /**
+ * Paints the specified value with this font.
+ * @param g
+ * @param value
+ * @param x
+ * @param y
+ * @param width
+ * @param justification
+ * @throws java.io.IOException
+ */
+ public void paintWrap(Graphics g, String value,
+ int x, int y, int width, int justification) throws IOException {
+
+ changeFontColor(name, color);
+ paintWrapString(g, value, name, x, y, width, justification);
+ }
+}
diff --git a/src/images/alarmmask.png b/src/images/alarmmask.png
new file mode 100644
index 0000000..f5e5fd0
Binary files /dev/null and b/src/images/alarmmask.png differ
diff --git a/src/images/archedbargraph.png b/src/images/archedbargraph.png
new file mode 100644
index 0000000..8cbd5f5
Binary files /dev/null and b/src/images/archedbargraph.png differ
diff --git a/src/images/attributeobjects.png b/src/images/attributeobjects.png
new file mode 100644
index 0000000..e5cbb15
Binary files /dev/null and b/src/images/attributeobjects.png differ
diff --git a/src/images/auxiliarycontrolobjects.png b/src/images/auxiliarycontrolobjects.png
new file mode 100644
index 0000000..c3f1d43
Binary files /dev/null and b/src/images/auxiliarycontrolobjects.png differ
diff --git a/src/images/auxiliaryfunction.png b/src/images/auxiliaryfunction.png
new file mode 100644
index 0000000..a67329e
Binary files /dev/null and b/src/images/auxiliaryfunction.png differ
diff --git a/src/images/auxiliaryinput.png b/src/images/auxiliaryinput.png
new file mode 100644
index 0000000..9ffca8b
Binary files /dev/null and b/src/images/auxiliaryinput.png differ
diff --git a/src/images/button.png b/src/images/button.png
new file mode 100644
index 0000000..9ef264e
Binary files /dev/null and b/src/images/button.png differ
diff --git a/src/images/check.png b/src/images/check.png
new file mode 100644
index 0000000..c2588b5
Binary files /dev/null and b/src/images/check.png differ
diff --git a/src/images/command.png b/src/images/command.png
new file mode 100644
index 0000000..5a17c72
Binary files /dev/null and b/src/images/command.png differ
diff --git a/src/images/container.png b/src/images/container.png
new file mode 100644
index 0000000..22042f3
Binary files /dev/null and b/src/images/container.png differ
diff --git a/src/images/datamask.png b/src/images/datamask.png
new file mode 100644
index 0000000..eeb0c2b
Binary files /dev/null and b/src/images/datamask.png differ
diff --git a/src/images/ellipse.png b/src/images/ellipse.png
new file mode 100644
index 0000000..018415e
Binary files /dev/null and b/src/images/ellipse.png differ
diff --git a/src/images/fillattribute.png b/src/images/fillattribute.png
new file mode 100644
index 0000000..c68269e
Binary files /dev/null and b/src/images/fillattribute.png differ
diff --git a/src/images/fixedbitmap.png b/src/images/fixedbitmap.png
new file mode 100644
index 0000000..ac4da4f
Binary files /dev/null and b/src/images/fixedbitmap.png differ
diff --git a/src/images/font128x128_.png b/src/images/font128x128_.png
new file mode 100644
index 0000000..371108a
Binary files /dev/null and b/src/images/font128x128_.png differ
diff --git a/src/images/font128x192_.png b/src/images/font128x192_.png
new file mode 100644
index 0000000..d0a6e3a
Binary files /dev/null and b/src/images/font128x192_.png differ
diff --git a/src/images/font12x16.png b/src/images/font12x16.png
new file mode 100644
index 0000000..608e737
Binary files /dev/null and b/src/images/font12x16.png differ
diff --git a/src/images/font12x16_.png b/src/images/font12x16_.png
new file mode 100644
index 0000000..acb8e3a
Binary files /dev/null and b/src/images/font12x16_.png differ
diff --git a/src/images/font16x16_.png b/src/images/font16x16_.png
new file mode 100644
index 0000000..3578511
Binary files /dev/null and b/src/images/font16x16_.png differ
diff --git a/src/images/font16x24_.png b/src/images/font16x24_.png
new file mode 100644
index 0000000..b36c78d
Binary files /dev/null and b/src/images/font16x24_.png differ
diff --git a/src/images/font24x32_.png b/src/images/font24x32_.png
new file mode 100644
index 0000000..b881d29
Binary files /dev/null and b/src/images/font24x32_.png differ
diff --git a/src/images/font32x32_.png b/src/images/font32x32_.png
new file mode 100644
index 0000000..b76cd1c
Binary files /dev/null and b/src/images/font32x32_.png differ
diff --git a/src/images/font32x48_.png b/src/images/font32x48_.png
new file mode 100644
index 0000000..cb051c7
Binary files /dev/null and b/src/images/font32x48_.png differ
diff --git a/src/images/font48x64_.png b/src/images/font48x64_.png
new file mode 100644
index 0000000..6f8e39b
Binary files /dev/null and b/src/images/font48x64_.png differ
diff --git a/src/images/font64x64_.png b/src/images/font64x64_.png
new file mode 100644
index 0000000..262f275
Binary files /dev/null and b/src/images/font64x64_.png differ
diff --git a/src/images/font64x96_.png b/src/images/font64x96_.png
new file mode 100644
index 0000000..09c7b62
Binary files /dev/null and b/src/images/font64x96_.png differ
diff --git a/src/images/font6x8.png b/src/images/font6x8.png
new file mode 100644
index 0000000..efacf61
Binary files /dev/null and b/src/images/font6x8.png differ
diff --git a/src/images/font6x8_.png b/src/images/font6x8_.png
new file mode 100644
index 0000000..28d6303
Binary files /dev/null and b/src/images/font6x8_.png differ
diff --git a/src/images/font8x12.png b/src/images/font8x12.png
new file mode 100644
index 0000000..d380748
Binary files /dev/null and b/src/images/font8x12.png differ
diff --git a/src/images/font8x12_.png b/src/images/font8x12_.png
new file mode 100644
index 0000000..4b97463
Binary files /dev/null and b/src/images/font8x12_.png differ
diff --git a/src/images/font8x8.png b/src/images/font8x8.png
new file mode 100644
index 0000000..004774b
Binary files /dev/null and b/src/images/font8x8.png differ
diff --git a/src/images/font8x8_.png b/src/images/font8x8_.png
new file mode 100644
index 0000000..7dc77b4
Binary files /dev/null and b/src/images/font8x8_.png differ
diff --git a/src/images/font96x128_.png b/src/images/font96x128_.png
new file mode 100644
index 0000000..f535318
Binary files /dev/null and b/src/images/font96x128_.png differ
diff --git a/src/images/fontattribute.png b/src/images/fontattribute.png
new file mode 100644
index 0000000..e515a7e
Binary files /dev/null and b/src/images/fontattribute.png differ
diff --git a/src/images/includeobject.png b/src/images/includeobject.png
new file mode 100644
index 0000000..db2c0b6
Binary files /dev/null and b/src/images/includeobject.png differ
diff --git a/src/images/inputattribute.png b/src/images/inputattribute.png
new file mode 100644
index 0000000..88b03dc
Binary files /dev/null and b/src/images/inputattribute.png differ
diff --git a/src/images/inputbooleanfield.png b/src/images/inputbooleanfield.png
new file mode 100644
index 0000000..3e04713
Binary files /dev/null and b/src/images/inputbooleanfield.png differ
diff --git a/src/images/inputfieldobjects.png b/src/images/inputfieldobjects.png
new file mode 100644
index 0000000..1d8f046
Binary files /dev/null and b/src/images/inputfieldobjects.png differ
diff --git a/src/images/inputlistfield.png b/src/images/inputlistfield.png
new file mode 100644
index 0000000..f095330
Binary files /dev/null and b/src/images/inputlistfield.png differ
diff --git a/src/images/inputnumberfield.png b/src/images/inputnumberfield.png
new file mode 100644
index 0000000..bafcfe4
Binary files /dev/null and b/src/images/inputnumberfield.png differ
diff --git a/src/images/inputstringfield.png b/src/images/inputstringfield.png
new file mode 100644
index 0000000..fc37cf1
Binary files /dev/null and b/src/images/inputstringfield.png differ
diff --git a/src/images/keyobjects.png b/src/images/keyobjects.png
new file mode 100644
index 0000000..1cde56c
Binary files /dev/null and b/src/images/keyobjects.png differ
diff --git a/src/images/language.png b/src/images/language.png
new file mode 100644
index 0000000..b0910e8
Binary files /dev/null and b/src/images/language.png differ
diff --git a/src/images/line.png b/src/images/line.png
new file mode 100644
index 0000000..3d3cfa5
Binary files /dev/null and b/src/images/line.png differ
diff --git a/src/images/linearbargraph.png b/src/images/linearbargraph.png
new file mode 100644
index 0000000..2d4fec5
Binary files /dev/null and b/src/images/linearbargraph.png differ
diff --git a/src/images/lineattribute.png b/src/images/lineattribute.png
new file mode 100644
index 0000000..2702412
Binary files /dev/null and b/src/images/lineattribute.png differ
diff --git a/src/images/macro.png b/src/images/macro.png
new file mode 100644
index 0000000..4f8ec06
Binary files /dev/null and b/src/images/macro.png differ
diff --git a/src/images/macroobjects.png b/src/images/macroobjects.png
new file mode 100644
index 0000000..6bbe187
Binary files /dev/null and b/src/images/macroobjects.png differ
diff --git a/src/images/meter.png b/src/images/meter.png
new file mode 100644
index 0000000..e154738
Binary files /dev/null and b/src/images/meter.png differ
diff --git a/src/images/nocheck.png b/src/images/nocheck.png
new file mode 100644
index 0000000..5c987cd
Binary files /dev/null and b/src/images/nocheck.png differ
diff --git a/src/images/numbervariable.png b/src/images/numbervariable.png
new file mode 100644
index 0000000..f0b3e4f
Binary files /dev/null and b/src/images/numbervariable.png differ
diff --git a/src/images/objectpointer.png b/src/images/objectpointer.png
new file mode 100644
index 0000000..37aef50
Binary files /dev/null and b/src/images/objectpointer.png differ
diff --git a/src/images/objectpoolobjects.png b/src/images/objectpoolobjects.png
new file mode 100644
index 0000000..d988e81
Binary files /dev/null and b/src/images/objectpoolobjects.png differ
diff --git a/src/images/outputfieldobjects.png b/src/images/outputfieldobjects.png
new file mode 100644
index 0000000..defc7f3
Binary files /dev/null and b/src/images/outputfieldobjects.png differ
diff --git a/src/images/outputgraphicobjects.png b/src/images/outputgraphicobjects.png
new file mode 100644
index 0000000..8cc3f37
Binary files /dev/null and b/src/images/outputgraphicobjects.png differ
diff --git a/src/images/outputnumberfield.png b/src/images/outputnumberfield.png
new file mode 100644
index 0000000..d906099
Binary files /dev/null and b/src/images/outputnumberfield.png differ
diff --git a/src/images/outputshapeobjects.png b/src/images/outputshapeobjects.png
new file mode 100644
index 0000000..4d9fd68
Binary files /dev/null and b/src/images/outputshapeobjects.png differ
diff --git a/src/images/outputstringfield.png b/src/images/outputstringfield.png
new file mode 100644
index 0000000..24da03a
Binary files /dev/null and b/src/images/outputstringfield.png differ
diff --git a/src/images/picturegraphic.png b/src/images/picturegraphic.png
new file mode 100644
index 0000000..f3f9156
Binary files /dev/null and b/src/images/picturegraphic.png differ
diff --git a/src/images/picturegraphicobjects.png b/src/images/picturegraphicobjects.png
new file mode 100644
index 0000000..5b04251
Binary files /dev/null and b/src/images/picturegraphicobjects.png differ
diff --git a/src/images/point.png b/src/images/point.png
new file mode 100644
index 0000000..1585467
Binary files /dev/null and b/src/images/point.png differ
diff --git a/src/images/pointerobjects.png b/src/images/pointerobjects.png
new file mode 100644
index 0000000..2eeed63
Binary files /dev/null and b/src/images/pointerobjects.png differ
diff --git a/src/images/polygon.png b/src/images/polygon.png
new file mode 100644
index 0000000..fc8f9be
Binary files /dev/null and b/src/images/polygon.png differ
diff --git a/src/images/pooleditlogo.png b/src/images/pooleditlogo.png
new file mode 100644
index 0000000..277964f
Binary files /dev/null and b/src/images/pooleditlogo.png differ
diff --git a/src/images/rectangle.png b/src/images/rectangle.png
new file mode 100644
index 0000000..59c0510
Binary files /dev/null and b/src/images/rectangle.png differ
diff --git a/src/images/softkey.png b/src/images/softkey.png
new file mode 100644
index 0000000..e43095d
Binary files /dev/null and b/src/images/softkey.png differ
diff --git a/src/images/softkeymask.png b/src/images/softkeymask.png
new file mode 100644
index 0000000..1f221dd
Binary files /dev/null and b/src/images/softkeymask.png differ
diff --git a/src/images/stringvariable.png b/src/images/stringvariable.png
new file mode 100644
index 0000000..5169e71
Binary files /dev/null and b/src/images/stringvariable.png differ
diff --git a/src/images/toplevelobjects.png b/src/images/toplevelobjects.png
new file mode 100644
index 0000000..b21315b
Binary files /dev/null and b/src/images/toplevelobjects.png differ
diff --git a/src/images/variableobjects.png b/src/images/variableobjects.png
new file mode 100644
index 0000000..02e8383
Binary files /dev/null and b/src/images/variableobjects.png differ
diff --git a/src/images/workingset.png b/src/images/workingset.png
new file mode 100644
index 0000000..67f8304
Binary files /dev/null and b/src/images/workingset.png differ
diff --git a/src/multidom/MultiDOM.java b/src/multidom/MultiDOM.java
new file mode 100644
index 0000000..ad5039e
--- /dev/null
+++ b/src/multidom/MultiDOM.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package multidom;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.tree.TreePath;
+import org.w3c.dom.Document;
+
+/**
+ *
+ * @author mohman
+ */
+public class MultiDOM {
+
+ private List documents;
+ private SingleDOM activeDocument;
+ private boolean newDocument = true;
+
+ /**
+ * Constructor.
+ */
+ public MultiDOM() {
+ this.documents = new ArrayList();
+ }
+
+ /**
+ * Constructor.
+ * @param documents
+ */
+ public MultiDOM(List documents) {
+ this.documents = documents;
+ }
+
+ /**
+ * Gets the active path.
+ * @return
+ */
+ public TreePath getActivePath() {
+ return this.activeDocument == null ? null : this.activeDocument.getActivePath();
+ }
+
+ /**
+ * Sets the active path.
+ * @param path
+ */
+ public void setActivePath(TreePath path) {
+ //this.activeDocument.setActivePath(path);
+ //firePathChange();
+ if (this.activeDocument.setActivePath(path) || newDocument) {
+ this.newDocument = false;
+ firePathChange();
+ }
+ }
+
+ /**
+ * Gets the active document.
+ * @return
+ */
+ public SingleDOM getActiveDocument() {
+ return this.activeDocument;
+ }
+
+ /**
+ * Sets the active document.
+ * @param doc
+ */
+ public void setActiveDocument(SingleDOM doc) {
+ if (this.activeDocument == doc) {
+ return;
+ }
+ this.activeDocument = doc;
+ this.newDocument = true;
+ fireDocChangeListeners();
+ }
+
+ /**
+ * Sets the active document.
+ * @param doc
+ */
+ public void setActiveDocument(Document doc) {
+ for (SingleDOM d : documents) {
+ if (d.actual().equals(doc)) {
+ setActiveDocument(d);
+ return;
+ }
+ }
+ throw new RuntimeException();
+ }
+
+ /**
+ * Gets all documents.
+ * @return
+ */
+ public List getAllDocuments() {
+ return this.documents;
+ }
+
+ /**
+ * Creates a new document.
+ * @return
+ */
+ public SingleDOM newDocument() {
+ SingleDOM doc = new SingleDOM(this);
+ documents.add(doc);
+ return doc;
+ }
+
+ /**
+ * Parses the active document.
+ * @param text
+ * @throws java.lang.Exception
+ */
+ public void parseActiveDocument(String text) throws Exception {
+ activeDocument.parseDocument(text);
+ fireDocChangeListeners();
+ }
+
+ /**
+ * Loads a document.
+ * @param name
+ * @return
+ * @throws java.lang.Exception
+ */
+ public SingleDOM loadDocument(String name) throws Exception {
+ SingleDOM doc = newDocument();
+ doc.setName(name);
+ doc.loadDocument(); // can throw Exception!
+ return doc;
+ }
+
+ /**
+ * Saves the specified document.
+ * @param doc
+ * @throws java.lang.Exception
+ */
+ public void saveDocument(SingleDOM doc) throws Exception {
+ check(doc);
+ doc.saveDocument(); // can throw Exception!
+ }
+
+ /**
+ * Saves the specified document with the given name.
+ * @param doc
+ * @param name
+ * @throws java.lang.Exception
+ */
+ public void saveAsDocument(SingleDOM doc, String name) throws Exception {
+ check(doc);
+ doc.setName(name);
+ doc.saveDocument(); // can throw Exception!
+ }
+
+ /**
+ * Saves all documents.
+ * @throws java.lang.Exception
+ */
+ public void saveAllDocuments() throws Exception {
+ String failure = "";
+ for (SingleDOM d : documents) {
+ try {
+ saveDocument(d);
+ }
+ catch (Exception e) {
+ failure += e.getMessage() + "\n";
+ }
+ }
+ if (!failure.isEmpty()) {
+ throw new Exception(failure);
+ }
+ }
+
+ /**
+ * Checks a document???
+ * @param doc
+ */
+ private void check(SingleDOM doc) {
+ if (!documents.contains(doc)) {
+ throw new IllegalArgumentException("illegal document");
+ }
+ }
+
+ private final Vector docChangeListeners =
+ new Vector();
+
+ /**
+ * Adds a listener.
+ * @param l
+ */
+ public void addDocumentChangeListener(ChangeListener l) {
+ if (!docChangeListeners.contains(l)) {
+ docChangeListeners.add(l);
+ }
+ }
+
+ /**
+ * Removes a listener.
+ * @param l
+ */
+ public void removeDocumentChangeListener(ChangeListener l) {
+ docChangeListeners.remove(l);
+ }
+
+ /**
+ * Fires a state changed event.
+ */
+ private void fireDocChangeListeners() {
+ ChangeEvent e = new ChangeEvent(this);
+ for (ChangeListener l : docChangeListeners) {
+ l.stateChanged(e);
+ }
+ }
+
+ private final Vector pathChangeListeners =
+ new Vector();
+
+ /**
+ * Adds a listener.
+ * @param l
+ */
+ public void addPathChangeListener(ChangeListener l) {
+ if (!pathChangeListeners.contains(l)) {
+ pathChangeListeners.add(l);
+ }
+ }
+
+ /**
+ * Removes a listener.
+ * @param l
+ */
+ public void removePathChangeListener(ChangeListener l) {
+ pathChangeListeners.remove(l);
+ }
+
+ /**
+ * Fires a state changed event.
+ */
+ public void firePathChange() {
+ ChangeEvent e = new ChangeEvent(this);
+ for (ChangeListener l : pathChangeListeners) {
+ l.stateChanged(e);
+ }
+ }
+}
diff --git a/src/multidom/SingleDOM.java b/src/multidom/SingleDOM.java
new file mode 100644
index 0000000..67c3de1
--- /dev/null
+++ b/src/multidom/SingleDOM.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package multidom;
+
+import java.io.File;
+import java.util.Vector;
+import javax.swing.JFileChooser;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.tree.TreePath;
+import org.w3c.dom.Document;
+import pooledit.Tools;
+import pooledit.Utils;
+import treemodel.XMLTreeModel;
+
+
+/**
+ *
+ * @author mohman
+ */
+public class SingleDOM {
+
+ private JFileChooser fc = new JFileChooser();
+ private final XMLTreeModel treeModel = new XMLTreeModel();
+
+ private final MultiDOM multidom;
+
+ private Document doc;
+ private TreePath activePath;
+
+ // Absolute paths are platform dependent!
+ // private static String SCHEMA = "C:\\pooledit\\schema\\iso11783.xsd";
+
+ // Relative paths are relative to the actual xml document?!?
+ // public static final String SCHEMA = "schema" + File.separator + "iso11783.xsd";
+
+ // Use schema stored in the jar file?
+ public static String SCHEMA = SingleDOM.class.getResource("/schema/iso11783.xsd").toString();
+
+ /**
+ * Creates a new instance of SingleDOM
+ * @param multidom
+ */
+ public SingleDOM(MultiDOM multidom) {
+ this.multidom = multidom;
+
+ // add FileNameExtensionFilter
+ fc.setFileFilter(new FileNameExtensionFilter("XML-file", "xml") );
+ }
+
+ /**
+ * Gets the dom document.
+ * @return
+ */
+ public Document actual() {
+ return this.doc;
+ }
+
+ /**
+ * Gets the tree model.
+ * @return
+ */
+ public XMLTreeModel getTreeModel() {
+ return treeModel;
+ }
+
+ /**
+ * Gets the file chooser.
+ * @return
+ */
+ public JFileChooser getJFileChooser(){
+ return fc;
+ }
+
+ /**
+ * Sets the file chooser.
+ * @param fc
+ */
+ public void setJFileChooser(JFileChooser fc) {
+ this.fc = fc;
+ }
+
+ /**
+ * Gets the active path.
+ * @return
+ */
+ public TreePath getActivePath() {
+ return this.activePath;
+ }
+
+ /**
+ * Sets the active path.
+ * @param activePath
+ * @return
+ */
+ public boolean setActivePath(TreePath activePath) {
+ if (Utils.equalObjects(this.activePath, activePath)) {
+ return false;
+ }
+ this.activePath = activePath;
+ firePathChange();
+ return true;
+ }
+
+ /**
+ * Sets the name of this document.
+ * @param name
+ */
+ public void setName(String name) {
+ fc.setSelectedFile(new File(name));
+ fireNameChange();
+ }
+
+ /**
+ * Gets the name of this document.
+ * @return
+ */
+ public String getName() {
+ File file = fc.getSelectedFile();
+ return file == null ? "New Document" : file.toURI().getPath();
+ }
+
+ /**
+ * Parses this document.
+ * @param text
+ * @throws java.lang.Exception
+ */
+ public void parseDocument(String text) throws Exception {
+ doc = Tools.parseDocument(text, SCHEMA);
+ treeModel.setDocument(doc);
+ }
+
+ /**
+ * Loads a document.
+ * @throws java.lang.Exception
+ */
+ public void loadDocument() throws Exception {
+ doc = Tools.loadDocument(getName(), SCHEMA);
+
+ // Map map = Tools.createNameMap(doc);
+ // Tools.createMissingNames(doc, map);
+ // Tools.removeNesting(doc);
+ Tools.createRoles(doc.getDocumentElement());
+ Tools.checkNaming(doc);
+ treeModel.setDocument(doc);
+ }
+
+ /**
+ * Saves this document.
+ * @throws java.lang.Exception
+ */
+ public void saveDocument() throws Exception {
+ Tools.saveDocument(getName(), doc);
+ }
+
+ private final Vector pathChangeListeners =
+ new Vector();
+
+ /**
+ * Adds a listener.
+ * @param l
+ */
+ public void addPathChangeListener(ChangeListener l) {
+ if (!pathChangeListeners.contains(l)) {
+ pathChangeListeners.add(l);
+ }
+ }
+
+ /**
+ * Removes a listener.
+ * @param l
+ * @return
+ */
+ public boolean removePathChangeListener(ChangeListener l) {
+ return pathChangeListeners.remove(l);
+ }
+
+ /**
+ * Fires a state changed event.
+ */
+ private void firePathChange() {
+ ChangeEvent e = new ChangeEvent(this);
+ for (ChangeListener l : pathChangeListeners) {
+ l.stateChanged(e);
+ }
+ }
+
+ private final Vector nameChangeListeners =
+ new Vector();
+
+ /**
+ * Adds a listener.
+ * @param l
+ */
+ public void addNameChangeListener(ChangeListener l) {
+ if (!nameChangeListeners.contains(l)) {
+ nameChangeListeners.add(l);
+ }
+ }
+
+ /**
+ * Removes a listener.
+ * @param l
+ * @return
+ */
+ public boolean removeNameChangeListener(ChangeListener l) {
+ return nameChangeListeners.remove(l);
+ }
+
+ /**
+ * Fires a state changed event.
+ */
+ private void fireNameChange() {
+ ChangeEvent e = new ChangeEvent(this);
+ for (ChangeListener l : nameChangeListeners) {
+ l.stateChanged(e);
+ }
+ }
+}
diff --git a/src/objecttree/ObjectTree.java b/src/objecttree/ObjectTree.java
new file mode 100644
index 0000000..eaf775f
--- /dev/null
+++ b/src/objecttree/ObjectTree.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objecttree;
+
+import static pooledit.Definitions.*;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import org.w3c.dom.Element;
+import java.awt.dnd.DragGestureRecognizer;
+import javax.swing.tree.TreeSelectionModel;
+import dragndrop.XMLTransferable;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DragGestureEvent;
+import java.awt.dnd.DragGestureListener;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceListener;
+import java.awt.dnd.DropTarget;
+import java.awt.dnd.DropTargetListener;
+import java.awt.dnd.InvalidDnDOperationException;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellEditor;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreePath;
+import pooledit.Utils;
+import treemodel.XMLTreeModel;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class ObjectTree extends JTree implements KeyListener {
+
+ /**
+ * Creates a new instance of ObjectTree.
+ * @param model
+ */
+ public ObjectTree(XMLTreeModel model) {
+ super(model);
+ // create renderer and editor
+ DefaultTreeCellRenderer renderer = new ObjectTreeCellRenderer();
+ DefaultTreeCellEditor editor = new ObjectTreeCellEditor(this, renderer);
+
+ // set renderer and editor
+ setEditable(true);
+ //setToggleClickCount(10); // default is two
+ setCellRenderer(renderer);
+ setCellEditor(editor);
+ getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+
+ //setEditable(true);
+ //setDragEnabled(true);
+
+ // create and set transfer handler
+ //ObjectTransferHandler handler = new ObjectTransferHandler();
+ //setTransferHandler(handler);
+
+ // create and set drop target
+ //ObjectDropTarget droptarget = new ObjectDropTarget(handler);
+ //setDropTarget(droptarget);
+
+ final DragSource dragSource = DragSource.getDefaultDragSource();
+ final DragSourceListener dsListener = new ObjectTreeDragSourceListener();
+
+ // component, action, listener
+ DragGestureRecognizer recognizer = dragSource.createDefaultDragGestureRecognizer(
+ this,
+ DnDConstants.ACTION_COPY |
+ DnDConstants.ACTION_MOVE |
+ DnDConstants.ACTION_LINK,
+ new DragGestureListener() {
+
+ @Override
+ public void dragGestureRecognized(DragGestureEvent e) {
+ if ((e.getDragAction() &
+ (DnDConstants.ACTION_COPY |
+ DnDConstants.ACTION_MOVE |
+ DnDConstants.ACTION_LINK)) == 0) {
+
+ return;
+ }
+ // category nodes cannot be dragged
+ TreePath path = getSelectionPath();
+ if (path == null) {
+ return;
+ }
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+ if (node.isType(OBJECTPOOL) ||
+ node.isType(CATEGORIES) ||
+ node.isType(SUBCATEGORIES)) {
+ return;
+ }
+ // accept only 1st mouse button
+ InputEvent ie = e.getTriggerEvent();
+ if (ie instanceof MouseEvent && ((MouseEvent) ie).getButton() != MouseEvent.BUTTON1) {
+ return;
+ }
+ /*
+ // make dragging somewhat less sensitive (google for "Bug ID 4244358"),
+ // in Windows there are typically 2 to 4 events (a mouse pressed followed
+ // by mouse one or more mouse dragged events) -> DOES NOT WORK TOO GREAT...
+ final int EVENT_LIMIT = 2;
+ int count = 0;
+ for (Iterator i = e.iterator(); count < EVENT_LIMIT && i.hasNext(); i.next()) {
+ count++;
+ }
+ if (count < EVENT_LIMIT) {
+ return;
+ }
+ */
+ // drag is recognized!
+ try {
+ e.startDrag(DragSource.DefaultCopyNoDrop,
+ new XMLTransferable(getSelectionPaths()),
+ dsListener);
+ }
+ catch (InvalidDnDOperationException idoe) {
+ idoe.printStackTrace();
+ }
+ }
+ });
+
+ final DropTargetListener dtListener = new ObjectTreeDropTargetListener();
+
+ // component, ops, listener, accepting
+ final DropTarget dropTarget = new DropTarget(
+ this,
+ DnDConstants.ACTION_COPY |
+ DnDConstants.ACTION_MOVE |
+ DnDConstants.ACTION_LINK,
+ dtListener,
+ true);
+
+ // set up popup menu
+ ObjectTreePopup objectTreePopup = new ObjectTreePopup(this);
+ this.addMouseListener(objectTreePopup);
+
+ // set up keylistener
+ this.addKeyListener(this);
+ }
+
+ /**
+ * Sets the active path.
+ * @param tp
+ */
+ public void setActivePath(TreePath tp) {
+ if (Utils.equalObjects(super.getSelectionPath(), tp)) {
+ return;
+ }
+ // System.out.println("ObjectTree: setActivePath: " + tp);
+ super.setSelectionPath(tp);
+
+ // use scroll pane if needed
+ scrollRowToVisible(getLeadSelectionRow());
+ // System.out.println(getClass().getName() + ": setActivePath(): " + tp);
+ }
+
+ /**
+ * Reacts to key types - does nothing.
+ * @param e
+ */
+ @Override
+ public void keyTyped(KeyEvent e) {
+ }
+
+ /**
+ * Reacts to key presses.
+ * @param e
+ */
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_DELETE) {
+ //System.out.println("DELETE: ");
+
+ TreePath path = super.getSelectionPath();
+
+ if (path != null && path.getPathCount() > 1) {
+
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+ Element actual = node.actual();
+ Element link = node.link();
+
+ // not all nodes can be deleted
+ if (node.isType(OBJECTS) ||
+ node.isType(LANGUAGE, POINT, FIXEDBITMAP, INCLUDE_OBJECT) ||
+ node.getType().startsWith(COMMAND)) {
+
+ // actual object (or broken link)
+ if (link == null || node.isType(INCLUDE_OBJECT)) {
+ actual.getParentNode().removeChild(actual);
+ }
+ // working link (link != null)
+ else {
+ link.getParentNode().removeChild(link);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Reacts to key releases - does nothing.
+ * @param e
+ */
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+}
diff --git a/src/objecttree/ObjectTreeCellEditor.java b/src/objecttree/ObjectTreeCellEditor.java
new file mode 100644
index 0000000..be73dea
--- /dev/null
+++ b/src/objecttree/ObjectTreeCellEditor.java
@@ -0,0 +1,121 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package objecttree;
+
+import static pooledit.Definitions.*;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.event.MouseEvent;
+import java.util.EventObject;
+import javax.swing.Icon;
+import javax.swing.JLabel;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellEditor;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreePath;
+import org.w3c.dom.Element;
+import pooledit.TreeEditPopup;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+class ObjectTreeCellEditor extends DefaultTreeCellEditor {
+
+ final Container container = new Container();
+ private XMLTreeNode node;
+
+ /**
+ * Constructor.
+ * @param tree
+ * @param renderer
+ */
+ public ObjectTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
+ super(tree, renderer);
+ container.setLayout(new BorderLayout());
+ }
+
+ /**
+ * Gets cell editor value.
+ * @return
+ */
+ @Override
+ public Object getCellEditorValue() {
+ String value = (String) super.getCellEditorValue();
+ try {
+ TreeEditPopup.renameObject(node, value);
+ }
+ catch (IllegalArgumentException ex) {
+ }
+ catch (IllegalStateException ex) {
+ }
+ return node;
+ }
+
+ /**
+ * Gets tree cell editor component.
+ * @param tree
+ * @param value
+ * @param sel
+ * @param expanded
+ * @param leaf
+ * @param row
+ * @return
+ */
+ @Override
+ public Component getTreeCellEditorComponent(JTree tree, Object value,
+ boolean sel, boolean expanded, boolean leaf, int row) {
+
+ node = (XMLTreeNode) value;
+
+ // get the name to be edited
+ Element actual = node.actual();
+ String name = actual.getAttribute(NAME);
+
+ // get the editor (which does not print the icon for some reason...)
+ Component editor = super.getTreeCellEditorComponent(tree, name, sel, expanded, leaf, row);
+
+ // get the icon and put everything in the container
+ Icon icon = node.link() != null ?
+ ObjectTreeCellRenderer.getIcon(ObjectTreeCellRenderer.LINK_ICONS, node.getType()) :
+ ObjectTreeCellRenderer.getIcon(ObjectTreeCellRenderer.ICONS, node.getType());
+ container.removeAll();
+ container.add(new JLabel(icon), BorderLayout.WEST);
+ container.add(editor, BorderLayout.EAST);
+
+ return container;
+ }
+
+ /**
+ * Checks, whether a cell is editable or not. Only elements with names
+ * should be editable.
+ * @param event
+ * @return
+ */
+ @Override
+ public boolean isCellEditable(EventObject event) {
+ // find out the path to the selected node
+ TreePath p = null;
+ if (event == null) {
+ p = tree.getSelectionPath();
+ }
+ else {
+ MouseEvent e = (MouseEvent) event;
+ p = tree.getClosestPathForLocation(e.getX(), e.getY());
+ }
+ // check if the node should be editable (i.e. it has a name)
+ XMLTreeNode n = (XMLTreeNode) p.getLastPathComponent();
+ Element actual = n.actual();
+ if (actual == null || !actual.hasAttribute(NAME)) {
+ return false;
+ }
+ else {
+ return super.isCellEditable(event);
+ }
+ }
+}
diff --git a/src/objecttree/ObjectTreeCellRenderer.java b/src/objecttree/ObjectTreeCellRenderer.java
new file mode 100644
index 0000000..3f068a7
--- /dev/null
+++ b/src/objecttree/ObjectTreeCellRenderer.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objecttree;
+
+import static pooledit.Definitions.*;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import javax.swing.Icon;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import treemodel.XMLTreeNode;
+import pooledit.Utils;
+
+/**
+ *
+ * @author mohman
+ */
+public class ObjectTreeCellRenderer extends DefaultTreeCellRenderer {
+
+ static Icon[] ICONS = new Icon[TREE_ELEMENTS.length];
+ static Icon[] LINK_ICONS = new Icon[TREE_ELEMENTS.length];
+
+ static class LinkIcon implements Icon {
+ private final Icon icon;
+ public LinkIcon(Icon icon) { this.icon = icon; }
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ icon.paintIcon(c, g, x, y);
+ int w = getIconWidth();
+ int w2 = w / 2;
+ int w8 = w / 8;
+ int h = getIconHeight();
+ int h2 = h / 2;
+ int h8 = h / 8;
+ g.setColor(Color.BLUE);
+ g.fillRect(w - w2, h - h2, w2, h2);
+ g.setColor(Color.WHITE);
+ g.drawLine(w - 3 * w8, h - 3 * h8, w - 3 * w8, h - h8);
+ g.drawLine(w - 3 * w8, h - h8, w - w8, h - h8);
+ }
+ @Override
+ public int getIconWidth() { return icon.getIconWidth(); }
+ @Override
+ public int getIconHeight() { return icon.getIconHeight(); }
+ }
+
+ static {
+ for (int i = 0, n = TREE_ELEMENTS.length; i < n; i++) {
+ ICONS[i] = Utils.createImageIcon(TREE_ELEMENT_PATH +
+ TREE_ELEMENT_FILENAMES[i]);
+ }
+ for (int i = 0, n = TREE_ELEMENTS.length; i < n; i++) {
+ LINK_ICONS[i] = new LinkIcon(ICONS[i]);
+ }
+ }
+
+ /**
+ * Gets icon.
+ * @param type
+ * @return
+ */
+ static public Icon getIcon(String type) {
+ return getIcon(ICONS, type);
+ }
+
+ /**
+ * Gets icon.
+ * @param icons
+ * @param type
+ * @return
+ */
+ static protected Icon getIcon(Icon[] icons, String type) {
+ if (type == null) {
+ return null;
+ }
+ for (int i = 0, n = TREE_ELEMENTS.length; i < n; i++) {
+ if (type.equals(TREE_ELEMENTS[i])) {
+ return icons[i];
+ }
+ }
+ return type.startsWith(COMMAND) ? icons[TREE_ELEMENTS.length - 1] : null;
+ }
+
+ /**
+ * Gets a tree cell renderer component.
+ * @param tree
+ * @param value
+ * @param sel
+ * @param expanded
+ * @param leaf
+ * @param row
+ * @param hasFocus
+ * @return
+ */
+ @Override
+ public Component getTreeCellRendererComponent(JTree tree, Object value,
+ boolean sel, boolean expanded, boolean leaf, int row,
+ boolean hasFocus) {
+
+ super.getTreeCellRendererComponent
+ (tree, value, sel, expanded, leaf, row, hasFocus);
+
+ XMLTreeNode node = ((XMLTreeNode) value);
+ Icon icon = node.link() != null ?
+ getIcon(LINK_ICONS, node.getType()) :
+ getIcon(ICONS, node.getType());
+
+ if (icon != null) {
+ setIcon(icon);
+ }
+ return this;
+ }
+}
diff --git a/src/objecttree/ObjectTreeDragSourceListener.java b/src/objecttree/ObjectTreeDragSourceListener.java
new file mode 100644
index 0000000..345438a
--- /dev/null
+++ b/src/objecttree/ObjectTreeDragSourceListener.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objecttree;
+
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceContext;
+import java.awt.dnd.DragSourceDragEvent;
+import java.awt.dnd.DragSourceDropEvent;
+import java.awt.dnd.DragSourceEvent;
+import java.awt.dnd.DragSourceListener;
+
+/**
+ *
+ * @author mohman
+ */
+public class ObjectTreeDragSourceListener implements DragSourceListener {
+
+ private final boolean DEBUG = false;
+
+ /**
+ * Creates a new instance of ObjectTreeDragListener
+ */
+ public ObjectTreeDragSourceListener() {
+ }
+
+ /**
+ * Prints debug messages.
+ * @param msg
+ */
+ private void dmsg(Object msg) {
+ if (DEBUG) {
+ System.out.println("[" + getClass().getName() + "]: " + msg);
+ }
+ }
+
+ @Override
+ public void dragEnter(DragSourceDragEvent e) {
+ dmsg("dragEnter");
+ //intersection of the users selected action, and the source and target actions
+ setCursor(e.getDropAction(), e.getDragSourceContext());
+ }
+
+ @Override
+ public void dragOver(DragSourceDragEvent e) {
+ DragSourceContext c = e.getDragSourceContext();
+ dmsg( "dragOver:" +
+ " source actions " + c.getSourceActions() +
+ " user action " + e.getUserAction() +
+ " drop actions " + e.getDropAction() +
+ " target actions " + e.getTargetActions());
+ }
+
+ @Override
+ public void dropActionChanged(DragSourceDragEvent e) {
+ dmsg("dropActionChanged");
+ setCursor(e.getDropAction(), e.getDragSourceContext());
+ }
+
+ /**
+ * Sets cursor.
+ * @param dropAction
+ * @param context
+ */
+ private void setCursor(int dropAction, DragSourceContext context) {
+ if (dropAction == DnDConstants.ACTION_COPY) {
+ context.setCursor(DragSource.DefaultCopyDrop);
+ }
+ else if (dropAction == DnDConstants.ACTION_MOVE) {
+ context.setCursor(DragSource.DefaultMoveDrop);
+ }
+ else if (dropAction == DnDConstants.ACTION_LINK) {
+ context.setCursor(DragSource.DefaultLinkDrop);
+ }
+ else {
+ context.setCursor(DragSource.DefaultCopyNoDrop);
+ }
+ }
+
+ @Override
+ public void dragExit(DragSourceEvent e) {
+ dmsg("dragExit");
+ DragSourceContext context = e.getDragSourceContext();
+ }
+
+ @Override
+ public void dragDropEnd(DragSourceDropEvent e) {
+ if (e.getDropSuccess() == false) {
+ dmsg("dragDropEnd (not ok)");
+ return;
+ }
+
+ /*
+ * the dropAction should be what the drop target specified
+ * in acceptDrop
+ */
+ dmsg("dragDropEnd (ok)");
+
+ // this is the action selected by the drop target
+ if (e.getDropAction() != DnDConstants.ACTION_MOVE) {
+ return;
+ }
+
+ Object source = e.getSource();
+ if (!(source instanceof ObjectTree)) {
+ return;
+ }
+
+ ObjectTree tree = (ObjectTree) source;
+
+ //System.out.println("REMOVE SOMETHING FROM THE TREE!");
+ }
+}
diff --git a/src/objecttree/ObjectTreeDropTargetListener.java b/src/objecttree/ObjectTreeDropTargetListener.java
new file mode 100644
index 0000000..714c0a9
--- /dev/null
+++ b/src/objecttree/ObjectTreeDropTargetListener.java
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objecttree;
+
+import static pooledit.Definitions.*;
+import org.w3c.dom.Document;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics2D;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.io.IOException;
+import java.io.StringReader;
+import javax.swing.JTree;
+import javax.swing.tree.TreePath;
+import org.w3c.dom.Element;
+import pooledit.PoolException;
+import pooledit.Tools;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class ObjectTreeDropTargetListener implements DropTargetListener {
+
+ private final boolean DEBUG = false;
+ private final Rectangle rect2D = new Rectangle();
+ private final Insets autoscrollInsets = new Insets(20, 20, 20, 20);
+
+ private Rectangle lastRowBounds;
+
+ // private ObjectTransferHandler handler;
+ private Point mostRecentLocation;
+ private int insertAreaHeight = 8;
+ private int acceptableActions = DnDConstants.ACTION_COPY |
+ DnDConstants.ACTION_MOVE | DnDConstants.ACTION_LINK;
+ private final DataFlavor stringFlavor;
+
+ /**
+ * Creates a new instance of ObjectTreeDropListener
+ */
+ public ObjectTreeDropTargetListener() {
+ String stringType = DataFlavor.javaJVMLocalObjectMimeType +
+ ";class=java.lang.String";
+ try {
+ stringFlavor = new DataFlavor(stringType);
+ }
+ catch (ClassNotFoundException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Prints debug messages.
+ * @param msg
+ */
+ private void dmsg(Object msg) {
+ if (DEBUG) {
+ System.out.println("[" + getClass().getName() + "]: " + msg);
+ }
+ }
+
+ /* ----------------- drag image painting start ------------------ */
+
+ /**
+ * Paint the dragged node
+ private final void paintImage(JTree tree, Point pt, TreePath[] paths) {
+ BufferedImage image = getDragImage(tree, paths);
+ if (image != null) {
+ tree.paintImmediately(rect2D.getBounds());
+ rect2D.setRect((int) pt.getX() - 15, (int) pt.getY() - 15,
+ image.getWidth(), image.getHeight());
+ tree.getGraphics().drawImage(image, (int) pt.getX() - 15,
+ (int) pt.getY() - 15, tree);
+ }
+ }
+
+ public BufferedImage getDragImage(JTree tree, TreePath[] paths) {
+ BufferedImage image = null;
+ try {
+ TreePath dragPath = paths[0];
+ if (dragPath != null) {
+
+ // FIXME: only the first node is drawn while dragging
+
+ Rectangle pathBounds = tree.getPathBounds(dragPath);
+ TreeCellRenderer r = tree.getCellRenderer();
+ XMLTreeModel m = (XMLTreeModel)tree.getModel();
+ boolean nIsLeaf = m.isLeaf(dragPath.getLastPathComponent());
+ JComponent lbl = (JComponent)r.getTreeCellRendererComponent(tree, dragPath.getLastPathComponent(), false,
+ tree.isExpanded(dragPath), nIsLeaf, 0, false);
+ lbl.setBounds(pathBounds);
+ image = new BufferedImage(lbl.getWidth(), lbl.getHeight(),
+ java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE);
+ Graphics2D graphics = image.createGraphics();
+ graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
+ lbl.setOpaque(false);
+ lbl.paint(graphics);
+ graphics.dispose();
+ }
+ }
+ catch (RuntimeException re) {
+ // nothing to be done?
+ }
+ return image;
+ }
+ */
+
+ /**
+ * Clear drawings
+ * @param tree
+ */
+ private final void clearImage(JTree tree) {
+ tree.paintImmediately(rect2D.getBounds());
+ }
+
+ /* ----------------- drag image painting end ------------------ */
+
+ /* ----------------- autoscroll implementation start ---------- */
+
+ /**
+ * Gets autoscroll insets.
+ * @return
+ */
+ private Insets getAutoscrollInsets() {
+ return autoscrollInsets;
+ }
+
+ /**
+ * Scroll visible tree parts when user drags outside an 'inner
+ * part' of the visible region.
+ * @param tree
+ * @param cursorLocation
+ */
+ private void autoscroll(JTree tree, Point cursorLocation) {
+ Insets insets = getAutoscrollInsets();
+ Rectangle outer = tree.getVisibleRect();
+ Rectangle inner = new Rectangle
+ (outer.x + insets.left, outer.y + insets.top,
+ outer.width - (insets.left+insets.right), outer.height - (insets.top+insets.bottom));
+ if (!inner.contains(cursorLocation)) {
+ Rectangle scrollRect = new Rectangle
+ (cursorLocation.x - insets.left, cursorLocation.y - insets.top,
+ insets.left + insets.right, insets.top + insets.bottom);
+ tree.scrollRectToVisible(scrollRect);
+ }
+ }
+
+ /* ----------------- autoscroll implementation end ---------- */
+
+ /* ----------------- insertion mark painting start ---------- */
+
+ /**
+ * Manage display of a drag mark either highlighting a node or drawing an
+ * insertion mark.
+ * @param tree
+ * @param location
+ */
+ public void updateDragMark(JTree tree, Point location) {
+ mostRecentLocation = location;
+ int row = tree.getRowForPath(tree.getClosestPathForLocation(location.x, location.y));
+ TreePath path = tree.getPathForRow(row);
+ if (path != null) {
+ Rectangle rowBounds = tree.getPathBounds(path);
+
+ // find out if we have to mark a tree node or if we have
+ // to draw an insertion marker
+ int rby = rowBounds.y;
+ int topBottomDist = insertAreaHeight / 2;
+ // x = top, y = bottom of insert area
+ Point topBottom = new Point(rby - topBottomDist,
+ rby + topBottomDist);
+
+ if (topBottom.x <= location.y && topBottom.y >= location.y) {
+ // we are inside an insertArea
+ paintInsertMarker(tree, location);
+ }
+ else {
+ // we are inside a node
+ markNode(tree, location);
+ }
+ }
+ }
+
+ /**
+ * Get the most recent mouse location, i.e. the drop location when
+ * called upon drop.
+ * @return the mouse location recorded most recently during a drag
+ * operation
+ */
+ public Point getMostRecentDragLocation() {
+ return mostRecentLocation;
+ }
+
+ /**
+ * Mark the node that is closest to the current mouse location.
+ * @param tree
+ * @param location
+ */
+ private void markNode(JTree tree, Point location) {
+ TreePath path = tree.getClosestPathForLocation(location.x, location.y);
+ if (path != null) {
+ if (lastRowBounds != null) {
+ Graphics2D g = (Graphics2D) tree.getGraphics();
+ g.setColor(Color.WHITE);
+ g.drawLine(lastRowBounds.x, lastRowBounds.y,
+ lastRowBounds.x + lastRowBounds.width, lastRowBounds.y);
+ }
+ tree.setSelectionPath(path);
+
+ // this will cause tree to expand as the object is dragged over
+ // it, very annoying! (there should be a small delay at least)
+ //tree.expandPath(path);
+ }
+ }
+
+ /**
+ * Paint an insert marker between the nodes closest to the current
+ * mouse location.
+ * @param tree
+ * @param location
+ */
+ private void paintInsertMarker(JTree tree, Point location) {
+ Graphics2D g = (Graphics2D) tree.getGraphics();
+ tree.clearSelection();
+ int row = tree.getRowForPath(tree.getClosestPathForLocation(location.x, location.y));
+ TreePath path = tree.getPathForRow(row);
+ if (path != null) {
+ Rectangle rowBounds = tree.getPathBounds(path);
+ if (lastRowBounds != null) {
+ g.setColor(Color.WHITE);
+ g.drawLine(lastRowBounds.x, lastRowBounds.y,
+ lastRowBounds.x + lastRowBounds.width, lastRowBounds.y);
+ }
+ if (rowBounds != null) {
+ g.setColor(Color.BLACK);
+ g.drawLine(rowBounds.x, rowBounds.y, rowBounds.x + rowBounds.width, rowBounds.y);
+ }
+ lastRowBounds = rowBounds;
+ }
+ }
+
+ /* ----------------- insertion mark painting end ------------------ */
+
+ @Override
+ public void dragEnter(DropTargetDragEvent e) {
+ if (isDragOk(e) == false) {
+ dmsg("dragEnter (not ok)");
+ e.rejectDrag();
+ return;
+ }
+ dmsg("dragEnter (ok)");
+ e.acceptDrag(e.getDropAction());
+ }
+
+ @Override
+ public void dragOver(DropTargetDragEvent e) {
+ JTree tree = (JTree) e.getDropTargetContext().getComponent();
+ Point loc = e.getLocation();
+ updateDragMark(tree, loc);
+
+ //XMLTransferable trans = (XMLTransferable) e.getTransferable();
+ //paintImage(tree, loc, trans.getPaths());
+
+ autoscroll(tree, loc);
+
+ if (isDragOk(e) == false) {
+ dmsg("dragOver (not ok)");
+ e.rejectDrag();
+ return;
+ }
+ dmsg("dragOver (ok)");
+ e.acceptDrag(e.getDropAction());
+ }
+
+ @Override
+ public void dropActionChanged(DropTargetDragEvent e) {
+ if (isDragOk(e) == false) {
+ dmsg("dropActionChanged (not ok)");
+ e.rejectDrag();
+ return;
+ }
+ dmsg("dropActionChanged (ok)");
+ e.acceptDrag(e.getDropAction());
+ }
+
+ @Override
+ public void dragExit(DropTargetEvent e) {
+ dmsg("dragExit");
+ clearImage((JTree) e.getDropTargetContext().getComponent());
+ }
+
+ @Override
+ public void drop(DropTargetDropEvent e) {
+ dmsg("drop");
+ clearImage((JTree) e.getDropTargetContext().getComponent());
+
+ DataFlavor chosen = chooseDropFlavor(e);
+ if (chosen == null) {
+ System.err.println("No flavor match found");
+ e.rejectDrop();
+ return;
+ }
+ dmsg("Chosen data flavor is " + chosen.getMimeType());
+
+ // the actual operation
+ int da = e.getDropAction();
+ // the actions that the source has specified with DragGestureRecognizer
+ int sa = e.getSourceActions();
+ dmsg("drop: sourceActions: " + sa + "\tdropAction: " + da);
+
+ if ((da & acceptableActions) == 0 ) {
+ dmsg("No action match found");
+ e.rejectDrop();
+ return;
+ }
+
+ Object data = null;
+ try {
+ /*
+ * the source listener receives this action in dragDropEnd.
+ * if the action is DnDConstants.ACTION_COPY_OR_MOVE then
+ * the source receives MOVE!
+ */
+ e.acceptDrop(da);
+ data = e.getTransferable().getTransferData(chosen);
+ if (data == null) {
+ throw new NullPointerException();
+ }
+ }
+ catch (Throwable t) {
+ System.err.println("Couldn't get transfer data: " + t.getMessage());
+ t.printStackTrace();
+ e.dropComplete(false);
+ return;
+ }
+ dmsg("got: (" + data.getClass().getName() + ")");
+
+ String result;
+ if (data instanceof String ) {
+ result = (String) data;
+ }
+ else if (data instanceof StringReader) {
+ StringReader rdr = (StringReader) data;
+ StringBuffer bff = new StringBuffer();
+ int in = -1;
+ try {
+ while ((in = rdr.read()) >= 0) {
+ if (in != 0) {
+ bff.append((char) in);
+ }
+ }
+ result = bff.toString();
+ }
+ catch (IOException ioe) {
+ /*
+ bug #4094987
+ sun.io.MalformedInputException: Missing byte-order mark
+ e.g. if dragging from MS Word 97 still a bug in 1.2 final
+ */
+ dmsg("cannot read " + ioe);
+ e.dropComplete(false);
+ return;
+ }
+ }
+ // incompatible data
+ else {
+ dmsg("incompatible data: (" + data.getClass() + ")");
+ e.dropComplete(false);
+ return;
+ }
+
+ Component target = e.getDropTargetContext().getComponent();
+ if (!(target instanceof JTree)) {
+ e.dropComplete(false);
+ return;
+ }
+
+ JTree tree = (JTree) target;
+ TreePath currentPath = tree.getSelectionPath();
+ boolean asSibling = (currentPath == null);
+
+ // if a node is selected, add child nodes to it
+ XMLTreeNode otherNode;
+ if (asSibling) {
+ // find sibling
+ Point location = e.getLocation();
+ TreePath closestPath = tree.getClosestPathForLocation(location.x, location.y);
+ otherNode = (XMLTreeNode) closestPath.getLastPathComponent();
+ }
+ else {
+ // if parent is a category node, stuff is added to the root
+ otherNode = (XMLTreeNode) currentPath.getLastPathComponent();
+ /*
+ if (otherNode.isType(CATEGORIES) || otherNode.isType(SUBCATEGORIES)) {
+ otherNode = (XMLTreeNode) otherNode.getModel().getRoot();
+ }
+ */
+ }
+ // if parent is not a real object (or a command), user root instead
+ if (!otherNode.isType(OBJECTS) && !otherNode.getType().startsWith(COMMAND)) {
+ otherNode = (XMLTreeNode) otherNode.getModel().getRoot();
+ asSibling = false;
+ }
+
+
+ Document doc = otherNode.getModel().getDocument();
+
+ // parse result
+ Element fragment = Tools.parseFragment(result, doc);
+ if (fragment == null) {
+ e.dropComplete(false);
+ return;
+ }
+
+ // implement the appropriate action (COPY, MOVE, LINK)
+ if (e.getDropAction() == DnDConstants.ACTION_COPY ||
+ e.getDropAction() == DnDConstants.ACTION_MOVE) {
+
+ // if copy has a name, it needs to be unique
+ String name = fragment.getAttribute(NAME);
+ if (!name.isEmpty()) {
+ String newname = Tools.findFreeName(name, Tools.createNameMap(doc, true));
+ fragment.setAttribute(NAME, newname);
+ }
+
+ e.dropComplete(Tools.insertFragment(fragment, otherNode.actual(), otherNode.link(), asSibling));
+ }
+ /*
+ else if (e.getDropAction() == DnDConstants.ACTION_MOVE) {
+
+ // moved object should retain its name, if there is no name
+ // conflict
+ String name = fragment.getAttribute(NAME);
+ if (Tools.createNameMap(doc).containsKey(name)) {
+ String newname = Tools.findFreeName(name, Tools.createNameMap(doc, true));
+ fragment.setAttribute(NAME, newname);
+ }
+
+ Tools.insertFragment(fragment, elem, asSibling);
+ e.dropComplete(true);
+ }
+ */
+ else if (e.getDropAction() == DnDConstants.ACTION_LINK) {
+
+ Element link = fragment.getOwnerDocument().createElement(INCLUDE_OBJECT);
+ Tools.copyAttributes(fragment, link, NAME, POS_X, POS_Y,
+ BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+
+ try {
+ if (Tools.createsLoop(link, otherNode.actual(), otherNode.link(), asSibling)) {
+ System.out.println("OPERATION ABORTED AS IT WOULD CREATE A LOOP!");
+ e.dropComplete(false);
+ }
+ else {
+ e.dropComplete(Tools.insertFragment(link, otherNode.actual(), otherNode.link(), asSibling));
+ }
+ }
+ catch (PoolException ex) {
+ e.dropComplete(false);
+ }
+ }
+ }
+
+ /**
+ * Checks to see if the flavor drag flavor is acceptable.
+ * @param e the DropTargetDragEvent object
+ * @return whether the flavor is acceptable
+ */
+ private boolean isDragFlavorSupported(DropTargetDragEvent e) {
+ boolean ok = false;
+ if (e.isDataFlavorSupported(DataFlavor.stringFlavor)) {
+ ok = true;
+ }
+ else if (e.isDataFlavorSupported(DataFlavor.plainTextFlavor)) {
+ ok = true;
+ }
+ else if (e.isDataFlavorSupported(stringFlavor)) { // shouldn't this be identical to stringFlavor (but it isn't)?
+ ok = true;
+ }
+ else {
+ dmsg("Supported flavors are:");
+ DataFlavor[] list = e.getCurrentDataFlavors();
+ for (int i = 0, n = list.length; i < n; i++) {
+ dmsg(list[i]);
+ }
+ dmsg("Ref");
+ dmsg(stringFlavor);
+ }
+ return ok;
+ }
+
+ /**
+ * Checks the flavors and operations.
+ * @param e the DropTargetDropEvent object
+ * @return the chosen DataFlavor or null if none match
+ */
+ private DataFlavor chooseDropFlavor(DropTargetDropEvent e) {
+ DataFlavor chosen = null;
+ if (e.isDataFlavorSupported(DataFlavor.stringFlavor)) {
+ chosen = DataFlavor.stringFlavor;
+ }
+ else if (e.isDataFlavorSupported(DataFlavor.plainTextFlavor)) {
+ chosen = DataFlavor.plainTextFlavor;
+ }
+ else if (e.isDataFlavorSupported(stringFlavor)) {
+ chosen = stringFlavor;
+ }
+ return chosen;
+ }
+
+ /**
+ * Checks the flavors and operations.
+ * @param e the event object
+ * @return whether the flavor and operation is ok
+ */
+ private boolean isDragOk(DropTargetDragEvent e) {
+ if (isDragFlavorSupported(e) == false) {
+ dmsg("isDragOk: (not ok)");
+ return false;
+ }
+
+ // the actions specified when the source
+ // created the DragGestureRecognizer
+ // int sa = e.getSourceActions();
+
+ // the docs on DropTargetDragEvent rejectDrag says that
+ // the dropAction should be examined
+ int da = e.getDropAction();
+ dmsg("dt drop action " + da + " my acceptable actions " + acceptableActions);
+
+ // we're saying that these actions are necessary
+ if ((da & acceptableActions) == 0) {
+ return false;
+ }
+ dmsg("isDragOk: (ok)");
+ return true;
+ }
+}
diff --git a/src/objecttree/ObjectTreePopup.java b/src/objecttree/ObjectTreePopup.java
new file mode 100644
index 0000000..9bf4f1a
--- /dev/null
+++ b/src/objecttree/ObjectTreePopup.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objecttree;
+
+import static pooledit.Definitions.*;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import javax.swing.tree.TreePath;
+import pooledit.TreeEditPopup;
+import treemodel.XMLTreeModel;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author Autlab
+ */
+public class ObjectTreePopup extends TreeEditPopup implements MouseListener {
+
+ private final ObjectTree objectTree;
+
+ /**
+ * Constructor.
+ * @param objectTree
+ */
+ public ObjectTreePopup(ObjectTree objectTree){
+ this.objectTree = objectTree;
+ }
+
+ /**
+ * Gets the current path.
+ * @return
+ */
+ @Override
+ public TreePath getCurrentPath() {
+ return objectTree.getSelectionPath();
+ }
+
+ /**
+ * Gets the xml tree model.
+ * @return
+ */
+ @Override
+ public XMLTreeModel getXMLTreeModel(){
+ return (XMLTreeModel) objectTree.getModel();
+ }
+
+ /**
+ * Reacts to mouse presses.
+ * @param e
+ */
+ @Override
+ public void mousePressed(MouseEvent e) {
+ maybeShowPopup(e);
+
+ // this is not absolutely necessary, but it makes starting of
+ // in-place editing much more predictable!
+ if (e.getClickCount() == 2) {
+ // get current path and start in-place editing
+ // (if the editor allows it for the selected node)
+ TreePath path = objectTree.getClosestPathForLocation(e.getX(), e.getY());
+ objectTree.startEditingAtPath(path);
+ }
+ }
+
+ /**
+ * Reacts to mouse releases.
+ * @param e
+ */
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ maybeShowPopup(e);
+ }
+
+ /**
+ * Shows popup menu when appropriate.
+ * @param e
+ */
+ private void maybeShowPopup(MouseEvent e) {
+ if (e.isPopupTrigger()) {
+ // popup trigger can change selection path
+ TreePath path = objectTree.getClosestPathForLocation(e.getX(), e.getY());
+ objectTree.setSelectionPath(path);
+
+ // the popup is shown only for real isobus objects (and some other pseudo objects)
+ // FIXME: how about commands?
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+ if (node.isType(OBJECTS) ||
+ node.isType(LANGUAGE, POINT, FIXEDBITMAP, INCLUDE_OBJECT) ||
+ node.getType().startsWith(COMMAND)) {
+ super.showPopup(e);
+ }
+ }
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) { }
+
+ @Override
+ public void mouseExited(MouseEvent e) { }
+}
diff --git a/src/objectview/GraphicObjectOperations.java b/src/objectview/GraphicObjectOperations.java
new file mode 100644
index 0000000..6599d56
--- /dev/null
+++ b/src/objectview/GraphicObjectOperations.java
@@ -0,0 +1,1148 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objectview;
+
+import color.ColorPalette;
+import font.BitmapFont;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.Stroke;
+import java.awt.TexturePaint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import pooledit.PictureConverter;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class GraphicObjectOperations implements ObjectOperations {
+
+ static private final int IMAGE_SIZE = 3;
+ static private final Paint PAINT = createPaint();
+ static private Paint createPaint() {
+ BufferedImage image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE,
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics2D gfx = (Graphics2D) image.getGraphics();
+ gfx.setColor(Color.YELLOW);
+ gfx.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE);
+ gfx.setColor(Color.RED);
+ gfx.drawLine(0, IMAGE_SIZE - 1, IMAGE_SIZE - 1, 0);
+ Rectangle anchor = new Rectangle(0, 0, IMAGE_SIZE, IMAGE_SIZE);
+ return new TexturePaint(image, anchor);
+ }
+ private final Stroke dashStroke = new BasicStroke(0.0f,
+ BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f,
+ new float[] {2.0f, 2.0f}, 0.0f);
+
+ private final Arc2D.Double arc = new Arc2D.Double();
+ private final Line2D.Double line = new Line2D.Double();
+ private final Rectangle2D.Double rect = new Rectangle2D.Double();
+ private final Ellipse2D.Double ellipse = new Ellipse2D.Double();
+
+ private int depth;
+ private boolean drawBorders;
+
+ private int colorDepth = ColorPalette.COLOR_8BIT;
+ private boolean reduceImages;
+
+ /* When flash is true, picture graphics objects are not drawn,
+ * if they have the flashing attribute set. */
+ private boolean flash;
+
+ /**
+ * Sets draw borders.
+ * @param drawBorders
+ */
+ public void setDrawBorders(boolean drawBorders) {
+ this.drawBorders = drawBorders;
+ }
+
+ /**
+ * Gets draw borders.
+ * @return
+ */
+ public boolean getDrawBorders() {
+ return drawBorders;
+ }
+
+ /**
+ * Sets color depth.
+ * @param colorDepth
+ */
+ public void setColorDepth(int colorDepth) {
+ if (colorDepth != ColorPalette.COLOR_1BIT &&
+ colorDepth != ColorPalette.COLOR_4BIT &&
+ colorDepth != ColorPalette.COLOR_8BIT) {
+ throw new IllegalArgumentException("color depth (" +
+ colorDepth + ") should be either 2, 16 or 256");
+ }
+ this.colorDepth = colorDepth;
+ }
+
+ /**
+ * Gets color depth.
+ * @return
+ */
+ public int getColorDepth() {
+ return colorDepth;
+ }
+
+ /**
+ * Sets reduce images flag.
+ * @param reduceImages
+ */
+ public void setReduceImages(boolean reduceImages) {
+ this.reduceImages = reduceImages;
+ }
+
+ /**
+ * Gets reduce images flag.
+ * @return
+ */
+ public boolean getReduceImages() {
+ return reduceImages;
+ }
+
+ /**
+ * Sets flash flag.
+ * @param flash
+ */
+ public void setFlash(boolean flash) {
+ this.flash = flash;
+ }
+
+ /**
+ * Gets flash flag.
+ * @return
+ */
+ public boolean getFlash() {
+ return this.flash;
+ }
+
+ /**
+ * Draws borders.
+ * @param gfx
+ * @param w
+ * @param h
+ */
+ private void drawBorders(Graphics2D gfx, int w, int h) {
+ if (drawBorders) {
+ Stroke olds = gfx.getStroke();
+ gfx.setStroke(dashStroke);
+
+ // using XOR mode is bad idea because it is quite common that
+ // two components have exactly the same borders (i.e. a container
+ // and some other component in it) in which case the resulting
+ // XOR operations cancel out
+
+ //gfx.setXORMode(Color.GREEN);
+ gfx.setColor(Color.GREEN);
+ rect.setRect(0, 0, w - 1, h - 1);
+ gfx.draw(rect);
+ gfx.setPaintMode();
+ gfx.setStroke(olds);
+ }
+ }
+
+ /**
+ * Constructor.
+ */
+ public GraphicObjectOperations() {
+ }
+
+ @Override
+ public void opWorkingSet(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, h);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opDataMask(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, h);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opAlarmMask(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, h);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opContainer(Graphics2D gfx, XMLTreeNode node) {
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opSoftKeyMask(Graphics2D gfx, XMLTreeNode node, int w, int h, int sk_height) {
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, h);
+
+ // paint the extra key
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(0, h - sk_height);
+ gfx.setColor(Color.BLACK);
+ gfx.fillPolygon(new int[] {w / 3, w / 3, 2 * w / 3},
+ new int[] {sk_height / 3, 2 * sk_height / 3, sk_height / 2}, 3);
+
+ drawBorders(gfx, w, sk_height);
+ gfx.setTransform(oldx);
+ }
+
+ @Override
+ public void opKey(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, h);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opButton(Graphics2D gfx, XMLTreeNode node) {
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ Color bgcolor = node.getBackgroundColor(colorDepth);
+
+ // fill back ground
+ gfx.setColor(bgcolor);
+ gfx.fillRect(0, 0, w, h);
+
+ // draw border
+ gfx.setColor(node.getBorderColor(colorDepth));
+ rect.setRect(0, 0, w - 1, h - 1);
+ gfx.draw(rect);
+
+ //draw "shadow"
+ gfx.fillPolygon(new int[] {0, w - 1, w - 1, w - 4, w - 4, 3},
+ new int[] {h - 1, h - 1, 0, 3, h - 4, h - 4}, 6);
+
+ // this could be just drawn but filling twice gives more consistent
+ // look at different zoom levels...
+ rect.setRect(3, 3, w - 7, h - 7);
+ gfx.fill(rect);
+ gfx.setColor(bgcolor);
+ rect.setRect(4, 4, w - 8, h - 8);
+ gfx.fill(rect);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opInputBoolean(Graphics2D gfx, XMLTreeNode node) {
+ int w = node.getWidth();
+
+ // fill background
+ if (!node.isOptionsTransparent()) {
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, w);
+ }
+
+ // paint mark (V)
+ if (node.getEffectiveValue() != 0) {
+ gfx.setColor(node.getFontAttributes().getFontColor(colorDepth));
+ gfx.fillPolygon(new int[] {w / 8, w / 10, w / 2, w - w / 10, w - w / 8, w / 2},
+ new int[] {w / 10, w / 8, w - w / 8, w / 8, w / 10, w / 2}, 6);
+ }
+
+ drawBorders(gfx, w, w);
+ }
+
+ @Override
+ public void opInputString(Graphics2D gfx, XMLTreeNode node) throws IOException {
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ XMLTreeNode fas = node.getFontAttributes();
+ BitmapFont font = fas.getFont(colorDepth);
+ Color backgroundColor = node.getBackgroundColor(colorDepth);
+
+ // invert colors
+ if (fas.isFontStyleInverted() ^
+ (fas.isFontStyleFlashingInverted() && flash)) {
+ Color tmp = font.getColor();
+ font.setColor(backgroundColor);
+ backgroundColor = tmp;
+ }
+
+ // fill background
+ if (!node.isOptionsTransparent()) {
+ gfx.setColor(backgroundColor);
+ gfx.fillRect(0, 0, w, h);
+ }
+
+ //read the value from stringvariable or the object
+ String value = node.getStringVariableValue();
+ if (value == null) {
+ value = node.getValue();
+ }
+
+ // paint string
+ if (!fas.isFontStyleFlashingHidden() || !flash) {
+ font.paint(gfx, value, 0, 0, w, node.getJustification());
+ }
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opInputNumber(Graphics2D gfx, XMLTreeNode node) throws IOException {
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ XMLTreeNode fas = node.getFontAttributes();
+ BitmapFont font = fas.getFont(colorDepth);
+ Color backgroundColor = node.getBackgroundColor(colorDepth);
+
+ // invert colors
+ if (fas.isFontStyleInverted() ^
+ (fas.isFontStyleFlashingInverted() && flash)) {
+ Color tmp = font.getColor();
+ font.setColor(backgroundColor);
+ backgroundColor = tmp;
+ }
+
+ // fill background
+ if (!node.isOptionsTransparent()) {
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, h);
+ }
+
+ // format number
+ String string = node.getFormatedNumber();
+
+ // the standard says that this option has effect only if "the value of
+ // the object is exactly zero", however it is unclear whether this refers
+ // to the visible value or the internal value
+ if (node.isOptionsBlankZero() &&
+ (node.getEffectiveValue() + node.getOffset() == 0)) {
+ //Double.parseDouble(string) == 0) {
+ return;
+ }
+
+ //add leading zeros if necessary
+ if (node.isOptionsDisplayLeadingZeros() && string.length() * font.getDimension().getWidth() < w) {
+ int zeros = (int) ((w-string.length() * font.getDimension().getWidth())) / (int) (font.getDimension().getWidth());
+ boolean negative = string.startsWith("-"); //val < 0.0);
+ if (negative) {
+ string = string.substring(1);
+ }
+ for (int i = 0; i < zeros; i++) {
+ string = "0" + string;
+ }
+ if (negative) {
+ string = "-" + string;
+ }
+ }
+
+ if (!fas.isFontStyleFlashingHidden() || !flash) {
+ font.paint(gfx, string, 0, 0, w, node.getJustification());
+ }
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opInputList(Graphics2D gfx, XMLTreeNode node) {
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ // this component does not have even a background!
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opOutputString(Graphics2D gfx, XMLTreeNode node) throws IOException {
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ XMLTreeNode fas = node.getFontAttributes();
+ BitmapFont font = fas.getFont(colorDepth);
+ Color backgroundColor = node.getBackgroundColor(colorDepth);
+
+ // invert colors
+ if (fas.isFontStyleInverted() ^
+ (fas.isFontStyleFlashingInverted() && flash)) {
+ Color tmp = font.getColor();
+ font.setColor(backgroundColor);
+ backgroundColor = tmp;
+ }
+
+ // fill background
+ if (!node.isOptionsTransparent()) {
+ gfx.setColor(backgroundColor);
+ gfx.fillRect(0, 0, w, h);
+ }
+
+ // read the value from stringvariable or the object
+ String value = node.getStringVariableValue();
+ if (value == null) {
+ value = node.getValue();
+ }
+
+ // paint string
+ if (!fas.isFontStyleFlashingHidden() || !flash) {
+ if (node.isOptionsAutoWrap()) {
+ font.paintWrap(gfx, value, 0, 0, w, node.getJustification());
+ } else {
+ font.paint(gfx, value, 0, 0, w, node.getJustification());
+ }
+ }
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opOutputNumber(Graphics2D gfx, XMLTreeNode node) throws IOException {
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ XMLTreeNode fas = node.getFontAttributes();
+ BitmapFont font = fas.getFont(colorDepth);
+ Color backgroundColor = node.getBackgroundColor(colorDepth);
+
+ // invert colors
+ if (fas.isFontStyleInverted() ^
+ (fas.isFontStyleFlashingInverted() && flash)) {
+ Color tmp = font.getColor();
+ font.setColor(backgroundColor);
+ backgroundColor = tmp;
+ }
+
+ // fill background
+ if (!node.isOptionsTransparent()) {
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, h);
+ }
+
+ // format number
+ String string = node.getFormatedNumber();
+
+ // the standard says that this option has effect only if "the value of
+ // the object is exactly zero", however it is unclear whether this refers
+ // to the visible value or the internal value
+ if (node.isOptionsBlankZero() &&
+ (node.getEffectiveValue() + node.getOffset() == 0)) {
+ //Double.parseDouble(string) == 0) {
+ return;
+ }
+
+ // add leading zeros if necessary
+ if (string.length() * font.getDimension().getWidth() < w && node.isOptionsDisplayLeadingZeros()) {
+ int zeros = (int)((w-string.length()*font.getDimension().getWidth())) / (int)(font.getDimension().getWidth());
+ boolean negative = string.startsWith("-"); //(val < 0.0);
+ if (negative) {
+ string = string.substring(1);
+ }
+ for (int i = 0; i < zeros; i++) {
+ string = "0" + string;
+ }
+ if (negative) {
+ string = "-" + string;
+ }
+ }
+
+ if (!fas.isFontStyleFlashingHidden() || !flash) {
+ font.paint(gfx, string, 0, 0, w, node.getJustification());
+ }
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opLine(Graphics2D gfx, XMLTreeNode node) {
+ Stroke olds = gfx.getStroke();
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ LineAttributes latt = node.getLineAttributes(colorDepth);
+ latt.apply(gfx);
+
+ float lw = ((BasicStroke) gfx.getStroke()).getLineWidth();
+ float lw2 = lw / 2;
+
+ // draw line
+ /*
+ if (!node.getLineDirection()) {
+ line.setLine(lw2, lw2, w - 1 + lw2, h - 1 + lw2);
+ } else {
+ line.setLine(lw2, h - 1 + lw2, w - 1 + lw2, lw2);
+ }*/
+ if (!node.getLineDirection()) {
+ line.setLine(lw2, lw2, w - lw + lw2, h - lw + lw2);
+ } else {
+ line.setLine(lw2, h - lw + lw2, w - lw + lw2, lw2);
+ }
+
+
+ gfx.draw(line);
+
+ gfx.setStroke(olds);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opRectangle(Graphics2D gfx, XMLTreeNode node, String imagepath) throws IOException {
+ Stroke olds = gfx.getStroke();
+ Paint oldp = gfx.getPaint();
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ LineAttributes latt = node.getLineAttributes(colorDepth);
+ Paint paint = node.getFillAttributesPaint(latt.getColor(), imagepath, PAINT, reduceImages, colorDepth);
+
+ // fill rectangle
+ if (paint != null) {
+ rect.setRect(0, 0, w, h);
+ gfx.setPaint(paint);
+ gfx.fill(rect);
+ }
+
+ // line suppression support
+ if (latt != null) {
+ float lw = ((BasicStroke) latt.getStroke()).getLineWidth();
+ float lw2 = lw / 2;
+ latt.apply(gfx);
+ if ((node.getLineSuppression() & 1) == 0) {
+ line.setLine(0, lw2, w, lw2);
+ gfx.draw(line);
+ }
+ if ((node.getLineSuppression() & 8) == 0) {
+ line.setLine(lw2, 0, lw2, h);
+ gfx.draw(line);
+ }
+ if ((node.getLineSuppression() & 4) == 0) {
+ line.setLine(0, h - lw2, w , h - lw2);
+ gfx.draw(line);
+ }
+ if ((node.getLineSuppression() & 2) == 0) {
+ line.setLine(w - lw2, 0, w - lw2, h);
+ gfx.draw(line);
+ }
+ }
+
+ gfx.setPaint(oldp);
+ gfx.setStroke(olds);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opEllipse(Graphics2D gfx, XMLTreeNode node, String imagepath) throws IOException {
+ Stroke olds = gfx.getStroke();
+ Paint oldp = gfx.getPaint();
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ int startAngle = node.getStartAngle();
+ int endAngle = node.getEndAngle();
+ if (startAngle > endAngle) {
+ endAngle += 360;
+ }
+ Integer ellipseType = node.getEllipseType();
+
+ LineAttributes latt = node.getLineAttributes(colorDepth);
+ Paint paint = node.getFillAttributesPaint(latt.getColor(), imagepath, PAINT, reduceImages, colorDepth);
+
+ float lw = ((BasicStroke) gfx.getStroke()).getLineWidth();
+ float lw2 = lw / 2;
+
+ // draw ellipse (null -> closed)
+ if (ellipseType == null) { //startAngle == endAngle) {
+ if (paint != null) {
+ ellipse.setFrame(0, 0, w - 1, h - 1);
+ gfx.setPaint(paint);
+ gfx.fill(ellipse);
+ }
+ if (latt != null) {
+ ellipse.setFrame(lw2, lw2, w - 1 - lw2, h - 1 - lw2);
+ latt.apply(gfx);
+ gfx.draw(ellipse);
+ }
+ }
+ // draw arc
+ else {
+ double angleStart = calcAngle(w, h, startAngle); //Angle*2?
+ double angleLength = calcAngle(w, h, endAngle) - angleStart;
+
+ // open ellipses are not fillable
+ if (ellipseType != Arc2D.OPEN && paint != null) {
+ gfx.setPaint(paint);
+ arc.setArc(0, 0, w - 1, h - 1, angleStart, angleLength, ellipseType);
+ gfx.fill(arc);
+ }
+ if (latt != null) {
+ latt.apply(gfx);
+ arc.setArc(lw2, lw2, w - 1 - lw2, h - 1 - lw2, angleStart, angleLength, ellipseType);
+ gfx.draw(arc);
+ }
+ }
+
+ gfx.setPaint(oldp);
+ gfx.setStroke(olds);
+
+ drawBorders(gfx, w, h);
+ }
+
+ // There is a problem with drawing thick lines if they don't fit inside the
+ // clipping rectangle.
+ @Override
+ public void opPolygon(Graphics2D gfx, XMLTreeNode node, String imagepath) throws IOException {
+ Stroke olds = gfx.getStroke();
+ Paint oldp = gfx.getPaint();
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ LineAttributes latt = node.getLineAttributes(colorDepth);
+ Paint paint = node.getFillAttributesPaint(latt.getColor(), imagepath, PAINT, reduceImages, colorDepth);
+ Polygon p = node.getPolygon();
+
+ float lw = ((BasicStroke) gfx.getStroke()).getLineWidth();
+
+ // draw open polygon
+ if (node.isPolygonTypeOpen()) {
+ if (latt != null) {
+ latt.apply(gfx);
+ int points[] = node.getPolygonPoints();
+ for (int i = 0, n = points.length - 2; i < n ; i += 2) {
+ gfx.drawLine(points[i], points[i + 1], points[i + 2], points[i + 3]);
+ }
+ }
+ }
+ // draw filled polgon
+ else {
+ if (paint != null) {
+ gfx.setPaint(paint);
+ gfx.fillPolygon(p);
+ }
+ if (latt != null) {
+ latt.apply(gfx);
+ gfx.drawPolygon(p);
+ }
+ }
+ gfx.setPaint(oldp);
+ gfx.setStroke(olds);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opMeter(Graphics2D gfx, XMLTreeNode node) {
+ Paint oldp = gfx.getPaint();
+ int w = node.getWidth();
+ int w2 = w / 2;
+
+ int startAngle = node.getStartAngle(); //*2?
+ int endAngle = node.getEndAngle(); //*2?
+ if (startAngle >= endAngle) {
+ endAngle += 360; //Important check
+ }
+
+ // draw border
+ if (node.isOptionsBorder()) {
+ Color borderColor = node.getBorderColor(colorDepth);
+ gfx.setColor(borderColor);
+ rect.setRect(0, 0, w - 1, w - 1);
+ gfx.draw(rect);
+ }
+
+ // draw arc
+ Color arcAndTickColor = node.getArcAndTickColor(colorDepth);
+ if (node.isOptionsArc()) {
+ gfx.setColor(arcAndTickColor);
+ arc.setArc(0, 0, w - 1, w - 1, startAngle, endAngle - startAngle, Arc2D.OPEN);
+ gfx.draw(arc);
+ }
+
+ // move origin to the center
+ gfx.translate(w2, w2);
+
+ // draw ticks
+ if (node.isOptionsTicks()) {
+ gfx.setColor(arcAndTickColor);
+ int ticks = node.getTicks();
+ if (ticks == 1) {
+ double angle = Math.toRadians((endAngle + startAngle) / 2);
+ double cos = Math.cos(-angle);
+ double sin = Math.sin(-angle);
+ line.setLine(4 * w / 10 * cos, 4 * w / 10 * sin,
+ (w - 1) / 2 * cos, (w - 1) / 2 * sin);
+ gfx.draw(line);
+ } else if (ticks > 1) {
+ double angle = Math.toRadians(startAngle);
+ double inc = Math.toRadians((endAngle - startAngle) / (ticks - 1.0));
+ for (int i = 0; i < ticks; i++, angle += inc) {
+ double cos = Math.cos(-angle);
+ double sin = Math.sin(-angle);
+ line.setLine(4 * w / 10 * cos, 4 * w / 10 * sin,
+ (w - 1) / 2 * cos, (w - 1) / 2 * sin);
+ gfx.draw(line);
+ }
+ }
+ }
+
+ // draw needle
+ Integer value = node.getEffectiveValue();
+ int minValue = node.getMinValue();
+ int maxValue = node.getMaxValue();
+ double needleAngle = !node.isOptionsClockwise() ?
+ Math.toRadians(interpolate(minValue, startAngle, maxValue, endAngle, value)) :
+ Math.toRadians(interpolate(minValue, endAngle, maxValue, startAngle, value));
+
+ gfx.setColor(node.getNeedleColor(colorDepth));
+ line.setLine(0, 0, w2 * Math.cos(-needleAngle), w2 * Math.sin(-needleAngle));
+ gfx.draw(line);
+
+ // restore coordinate system
+ gfx.translate(-w2, -w2);
+
+ // restore paint
+ gfx.setPaint(oldp);
+
+ drawBorders(gfx, w, w);
+ }
+
+ @Override
+ public void opLinearBarGraph(Graphics2D gfx, XMLTreeNode node) {
+ Paint oldp = gfx.getPaint();
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ int minValue = node.getMinValue();
+ int maxValue = node.getMaxValue();
+
+ Color color = node.getColor(colorDepth);
+ Integer value = node.getEffectiveValue();
+
+ // filled
+ if (!node.isOptionsNoFill() ){
+ gfx.setColor(color);
+ // horizontal
+ if (node.isOptionsHorizontal()) {
+ if (node.isOptionsGrowPositive()) {
+ double x = interpolate(minValue, 0, maxValue, w - 1, value);
+ rect.setRect(0, 0, x, h - 1);
+ gfx.fill(rect);
+ } else {
+ double x = interpolate(minValue, w - 1, maxValue, 0, value);
+ rect.setRect(x, 0, w - 1 - x, h - 1);
+ gfx.fill(rect);
+ }
+ }
+ // vertical
+ else {
+ if (node.isOptionsGrowPositive()) {
+ double y = interpolate(minValue, h - 1, maxValue, 0, value);
+ rect.setRect(0, y, w - 1, h - 1 - y);
+ gfx.fill(rect);
+ } else {
+ double y = interpolate(minValue, 0, maxValue, h - 1, value);
+ rect.setRect(0, 0, w - 1, y);
+ gfx.fill(rect);
+ }
+ }
+ }
+ // not filled
+ else {
+ gfx.setColor(color);
+ // horizontal
+ if (node.isOptionsHorizontal()) {
+ double x = node.isOptionsGrowPositive() ?
+ interpolate(minValue, 0, maxValue, w - 1, value) :
+ interpolate(minValue, w - 1, maxValue, 0, value);
+
+ line.setLine(x, 0, x, h - 1);
+ gfx.draw(line);
+ }
+ // vertical
+ else {
+ double y = node.isOptionsGrowPositive() ?
+ interpolate(minValue, h - 1, maxValue, 0, value) :
+ interpolate(minValue, 0, maxValue, h - 1, value);
+
+ line.setLine(0, y, w - 1, y);
+ gfx.draw(line);
+ }
+ }
+
+ // drawing ticks
+ if (node.isOptionsTicks()) {
+ gfx.setColor(color);
+ int ticks = node.getTicks();
+ if (ticks == 1) {
+ if (node.isOptionsHorizontal()) {
+ double x = (w - 1.0) / 2.0;
+ line.setLine(x, 0, x, (h - 1) / 4);
+ gfx.draw(line);
+ } else {
+ double y = (h - 1.0) / 2.0;
+ line.setLine(0, y, (w - 1) / 4, y);
+ gfx.draw(line);
+ }
+ }
+ if (ticks > 1) {
+ if (node.isOptionsHorizontal()) {
+ double x = 0;
+ double inc = (w - 1.0) / (ticks - 1.0);
+ for (int tick = 0; tick < ticks; tick++, x += inc) {
+ line.setLine(x, 0, x, (h - 1) / 4);
+ gfx.draw(line);
+ }
+ } else {
+ double y = 0;
+ double inc = (h - 1.0) / (ticks - 1.0);
+ for (int tick = 0; tick < ticks; tick++, y += inc) {
+ line.setLine(0, y, (w - 1) / 4, y);
+ gfx.draw(line);
+ }
+ }
+ }
+ }
+
+ // border is drawn before target line, otherwise the target line
+ // is not visible at the min and max positions
+ if (node.isOptionsBorder()) {
+ gfx.setColor(color);
+ rect.setRect(0, 0, w - 1, h - 1);
+ gfx.draw(rect);
+ }
+
+ // drawing targetline
+ if (node.isOptionsTargetLine()) {
+ gfx.setColor(node.getTargetLineColor(colorDepth));
+ Integer targetLineValue = node.getEffectiveTargetValue();
+ if (node.isOptionsHorizontal()) {
+ double x = node.isOptionsGrowPositive() ?
+ interpolate(minValue, 0, maxValue, w - 1, targetLineValue) :
+ interpolate(minValue, w - 1, maxValue, 0, targetLineValue);
+
+ line.setLine(x, 0, x, h - 1);
+ gfx.draw(line);
+ } else {
+ double y = node.isOptionsGrowPositive() ?
+ interpolate(minValue, h - 1, maxValue, 0, targetLineValue) :
+ interpolate(minValue, 0, maxValue, h - 1, targetLineValue);
+
+ line.setLine(0, y, w - 1, y);
+ gfx.draw(line);
+ }
+ }
+
+ gfx.setPaint(oldp);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opArchedBarGraph(Graphics2D gfx, XMLTreeNode node) {
+ Paint oldp = gfx.getPaint();
+ int w = node.getWidth();
+ int h = node.getHeight();
+
+ int minValue = node.getMinValue();
+ int maxValue = node.getMaxValue();
+ int startAngle = node.getStartAngle();
+ int endAngle = node.getEndAngle();
+ if (startAngle > endAngle) {
+ endAngle += 360;
+ }
+
+ // bar width is intentionally converted to double here to force
+ // some calculation to use double operations
+ double barw = node.getBargraphWidth();
+
+ // draw border
+ gfx.setColor(node.getColor(colorDepth));
+ if (node.isOptionsBorder()) {
+ double angleStart = calcAngle(w, h, startAngle);
+ double angleEnd = calcAngle(w, h, endAngle);
+ arc.setArc(0, 0, w - 1, h - 1, angleStart, angleEnd - angleStart, Arc2D.OPEN);
+ gfx.draw(arc);
+ arc.setArc(barw, h * barw / w, w - 1 - 2 * barw, h * (w - 1 - 2 * barw) / w, angleStart, angleEnd - angleStart, Arc2D.OPEN);
+ gfx.draw(arc);
+ gfx.draw(setupLine(line, startAngle, w, h, barw));
+ gfx.draw(setupLine(line, endAngle, w, h, barw));
+ }
+
+ // draw value either as a filled region or just a simple line
+ Integer value = node.getEffectiveValue();
+ // no fill option is not specified -> fill
+ if (!node.isOptionsNoFill()){
+ double angleS;
+ double angleE;
+ if (!node.isOptionsClockwise()) {
+ angleS = calcAngle(w, h, startAngle);
+ angleE = calcAngle(w, h, interpolate(minValue, startAngle, maxValue, endAngle, value));
+ } else {
+ angleS = calcAngle(w, h, interpolate(minValue, endAngle, maxValue, startAngle, value));
+ angleE = calcAngle(w, h, endAngle);
+ }
+
+ arc.setArc(0, 0, w - 1, h - 1, angleS, angleE - angleS, Arc2D.PIE);
+ Area arc1 = new Area(arc);
+ arc.setArc(barw, barw / w * h, w - 1 - 2 * barw, (w - 1 - 2 * barw) / w * h, 0, 360, Arc2D.CHORD);
+ Area arc2 = new Area(arc);
+ arc1.subtract(arc2);
+ gfx.fill(arc1);
+ }
+ // no fill option is specified -> no fill
+ else {
+ double angle = !node.isOptionsClockwise() ?
+ interpolate(minValue, startAngle, maxValue, endAngle, value) :
+ interpolate(minValue, endAngle, maxValue, startAngle, value);
+
+ gfx.draw(setupLine(line, angle, w, h, barw));
+ }
+
+ // draw target line
+ if (node.isOptionsTargetLine()) {
+ gfx.setColor(node.getTargetLineColor(colorDepth));
+ Integer targetLineValue = node.getEffectiveTargetValue();
+ double angle = !node.isOptionsClockwise() ?
+ interpolate(minValue, startAngle, maxValue, endAngle, targetLineValue) :
+ interpolate(minValue, endAngle, maxValue, startAngle, targetLineValue);
+
+ gfx.draw(setupLine(line, angle, w, h, barw));
+ }
+
+ gfx.setPaint(oldp);
+
+ drawBorders(gfx, w, h);
+ }
+
+ /**
+ * A convenience method for setting up lines for arched bar graph.
+ * @param line
+ * @param angle
+ * @param w
+ * @param h
+ * @param barw
+ * @return
+ */
+ static private Line2D.Double setupLine(Line2D.Double line, double angle, int w, int h, double barw) {
+ double ang = Math.toRadians(calcAngle(w, h, angle));
+ double cos = Math.cos(-ang);
+ double sin = Math.sin(-ang);
+ line.setLine((cos + 1) * (w - 1) / 2.0,
+ (sin + 1) * (h - 1) / 2.0,
+ cos * (((w - 1) - 2 * barw) / 2.0) + (w - 1) / 2.0,
+ sin * ((w - 2 * barw) * (h - 1) / (2.0 * (w - 1))) + (h - 1) / 2.0);
+ return line;
+ }
+
+ @Override
+ public void opPictureGraphic(Graphics2D gfx, XMLTreeNode node, BufferedImage image) {
+ int w = node.getWidth();
+ int h = w * image.getHeight() / image.getWidth();
+ Color transparencyColor = node.getTransparencyColor(reduceImages, colorDepth);
+
+ image = PictureConverter.applyTransparencyAndReduceColors(image,
+ transparencyColor, node.isOptionsTransparent(),
+ reduceImages, colorDepth);
+
+ if (!node.isOptionsFlashing() || !flash) {
+ gfx.drawRenderedImage(image, new AffineTransform((float) w / (float) image.getWidth(),
+ 0f, 0f, (float) h / (float) image.getHeight(), 0f, 0f));
+ }
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opObjectPointer(Graphics2D gfx, XMLTreeNode node) {
+ // not much to do here
+ }
+
+ @Override
+ public void opAuxiliaryFunction(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ // fill back ground
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, h);
+
+ drawBorders(gfx, w, h);
+ }
+
+ @Override
+ public void opAuxiliaryInput(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ // fill back ground
+ gfx.setColor(node.getBackgroundColor(colorDepth));
+ gfx.fillRect(0, 0, w, h);
+
+ drawBorders(gfx, w, h);
+ }
+
+ /**
+ * Sets depth.
+ * @param depth
+ */
+ @Override
+ public void setDepth(int depth) {
+ this.depth = depth;
+ }
+
+ /**
+ * Increments depth by one.
+ */
+ @Override
+ public void incDepth() {
+ depth++;
+ }
+
+ /**
+ * Decrements depth by one.
+ */
+ @Override
+ public void decDepth() {
+ depth--;
+ }
+
+ /**
+ * Gets depth.
+ * @return
+ */
+ @Override
+ public int getDepth() {
+ return depth;
+ }
+
+ /**
+ * Linear interpolation between points p0 and p1. Returns y for the
+ * specified x. The output is limited to the interval [y0, y1].
+ * @param x0
+ * @param y0
+ * @param x1
+ * @param y1
+ * @param x
+ * @return
+ */
+ static public double interpolate(double x0, double y0, double x1, double y1, double x) {
+ if (x <= x0) {
+ return y0;
+ } else if (x >= x1) {
+ return y1;
+ } else {
+ double k = (y1 - y0) / (x1 - x0);
+ return k * (x - x0) + y0;
+ }
+ }
+
+ /*
+ static public double calcAng2(double a, double b, double ang) {
+ int cycle = 0;
+ int quadrant;
+ while (ang < 0) {
+ ang += 360;
+ cycle++;
+ }
+ while (ang >= 360) {
+ ang -= 360;
+ cycle--;
+ }
+ if (ang < 90) {
+ quadrant = 0;
+ }
+ else if (ang < 180) {
+ quadrant = 1;
+ ang = 180 - ang;
+ }
+ else if (ang < 270) {
+ quadrant = 2;
+ ang -= 180;
+ }
+ else {
+ quadrant = 3;
+ ang = 360 - ang;
+ }
+ ang = Math.toRadians(ang);
+ double t1 = Math.tan(ang);
+ double t2 = a * a * t1 * t1 + b * b;
+ double t3 = Math.sqrt(t2);
+ double t4 = Math.toDegrees(Math.acos(b / t3));
+ switch (quadrant) {
+ case 0:
+ return t4 - 360 * cycle;
+ case 1:
+ return 180 - t4 - 360 * cycle;
+ case 2:
+ return t4 + 180 - 360 * cycle;
+ case 3:
+ return 360 - t4 - 360 * cycle;
+ default:
+ throw new RuntimeException();
+ }
+ }
+ */
+
+ /**
+ * The parametric representation for an ellipse is:
+ * (cos(t) / a)^2 + (sin(t) / b)^2 = 1, t = [0, 360]
+ * Although the parameter t is in degrees, it is not the angle one might
+ * expect. This method takes in the parameters of the ellipse (a and b)
+ * and the true angle and returns the "parametric angle".
+ * @param a
+ * @param b
+ * @param ang
+ * @return
+ */
+ static public double calcAngle(double a, double b, double ang) {
+ int cycle = 0;
+ boolean mirror = false;
+ while (ang < -90) {
+ ang += 360;
+ cycle++;
+ }
+ while (ang >= 270) {
+ ang -= 360;
+ cycle--;
+ }
+ if (ang >= 90) {
+ mirror = true;
+ ang -= 180;
+ }
+ double t1 = a / b * Math.tan(Math.toRadians(ang));
+ double t2 = Math.toDegrees(Math.atan(t1)) - 360 * cycle;
+ return mirror ? t2 + 180 : t2;
+ }
+}
diff --git a/src/objectview/HelpGrid.java b/src/objectview/HelpGrid.java
new file mode 100644
index 0000000..eae2ab3
--- /dev/null
+++ b/src/objectview/HelpGrid.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objectview;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.List;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class HelpGrid {
+
+ private final Stroke DASH_STROKE = new BasicStroke(0.0f,
+ BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f,
+ new float[] {1.0f, 1.0f}, 0.0f);
+
+ private final List lines = new ArrayList();
+ private final AffineTransform xform = new AffineTransform();
+ private Rectangle2D rect;
+
+ double dx = 10;
+ double dy = 10;
+ double ox = 0;
+ double oy = 0;
+
+ /**
+ * Constructor.
+ */
+ public HelpGrid() {
+ }
+
+ public void reset() {
+ rect = null;
+ }
+
+ public boolean isSet() {
+ return rect != null;
+ }
+
+ public void makeLines(double width, double height) {
+
+ lines.clear();
+
+ // add vertical lines
+ for (double x = ox; x < width; x += dx) {
+ lines.add(new Line2D.Double(x, 0, x, height));
+ }
+
+ // add horizontal lines
+ for (double y = oy; y < height; y += dy) {
+ lines.add(new Line2D.Double(0, y, width, y));
+ }
+ }
+
+ public void set(AffineTransform xfrm, Shape shape, XMLTreeNode source) {
+ this.xform.setTransform(xfrm);
+ rect = xform.createTransformedShape(shape).getBounds2D();
+
+ Rectangle2D r = shape.getBounds2D();
+ makeLines(r.getWidth(), r.getHeight());
+ }
+
+ public void adjust(AffineTransform xfrm) {
+ if (isSet()) {
+ xform.preConcatenate(xfrm);
+ rect = xform.createTransformedShape(rect).getBounds2D();
+ }
+ }
+
+ /**
+ * Draws the help grid.
+ * @param gfx
+ * @param zoom
+ */
+ public void draw(Graphics2D gfx, double zoom) {
+ Stroke olds = gfx.getStroke();
+ gfx.setStroke(DASH_STROKE);
+ gfx.setXORMode(Color.PINK);
+ for (Line2D.Double line : lines) {
+ gfx.draw(xform.createTransformedShape(line));
+ }
+ gfx.setPaintMode();
+ gfx.setStroke(olds);
+ }
+}
diff --git a/src/objectview/LineAttributes.java b/src/objectview/LineAttributes.java
new file mode 100644
index 0000000..83491a4
--- /dev/null
+++ b/src/objectview/LineAttributes.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objectview;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Stroke;
+
+/**
+ *
+ * @author mohman
+ */
+public class LineAttributes {
+ private final Color color;
+ private final Stroke stroke;
+
+ /**
+ * Gets the line color.
+ * @return
+ */
+ public Color getColor() {
+ return color;
+ }
+
+ /**
+ * Gets the line stroke.
+ * @return
+ */
+ public Stroke getStroke() {
+ return stroke;
+ }
+
+ /**
+ * Creates a new instance of LineAttributes.
+ * @param color
+ * @param stroke
+ */
+ public LineAttributes(Color color, Stroke stroke) {
+ this.color = color;
+ this.stroke = stroke;
+ }
+
+ /**
+ * Applies these line attributes to the given graphics context.
+ * @param gfx
+ */
+ public void apply(Graphics2D gfx) {
+ gfx.setColor(color);
+ gfx.setStroke(stroke);
+ }
+}
diff --git a/src/objectview/MouseController.java b/src/objectview/MouseController.java
new file mode 100644
index 0000000..e9d2006
--- /dev/null
+++ b/src/objectview/MouseController.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objectview;
+
+import java.awt.Container;
+import javax.swing.JViewport;
+import static pooledit.Definitions.*;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import javax.swing.JScrollPane;
+import javax.swing.tree.TreePath;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class MouseController implements MouseListener, MouseMotionListener, MouseWheelListener {
+
+ private final double MAX_ZOOM_FACTOR = 10;
+ private final double MIN_ZOOM_FACTOR = 0.5;
+
+ /** Link to the parent view */
+ private ObjectView view;
+
+ /** The original rectangle (before editing starts) */
+ private Rectangle oldrect;
+
+ /** The original point (location of polygon corner) */
+ private Point oldpoint;
+
+ /** Deltas during dragging and final release are calculated against this
+ * point */
+ private Point point;
+
+ /** Popup window that is opened*/
+ private ObjectViewPopup objectViewPopup;
+
+ /** Keeps child object stationary, when the parent is expanded left / up */
+ private boolean smartMoves = true;
+
+ /** Creates a new instance of MouseController */
+ public MouseController(ObjectView view, ObjectViewPopup objectViewPopup) {
+ this.view = view;
+ this.objectViewPopup = objectViewPopup;
+ }
+
+ @Override
+ public void mouseDragged(MouseEvent e) {
+ if (point == null) {
+ return;
+ }
+ SelectionRectangle selrect = view.getSelectionRectangle();
+ int x = e.getX();
+ int y = e.getY();
+ if (oldpoint != null) {
+ selrect.moveSelectedPolyCorner(x - point.x, y - point.y);
+ }
+ if (oldrect != null) {
+ selrect.moveSelectedCorner(x - point.x, y - point.y);
+ }
+ point = e.getPoint();
+ view.repaint();
+ }
+
+ @Override
+ public void mouseMoved(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+
+ SelectionRectangle selrect = view.getSelectionRectangle();
+ if (e.isPopupTrigger()) {
+ objectViewPopup.showPopup(e);
+ selrect.deselectCorners();
+ return;
+ }
+ //this makes it possible for objectview to listen the keys
+ view.requestFocusInWindow();
+
+ int x = e.getX();
+ int y = e.getY();
+
+ if (selrect.isSet() && selrect.selectPolyCornerAt(x, y)) {
+ point = e.getPoint();
+ oldpoint = e.getPoint();
+ view.repaint();
+ return;
+ }
+ oldpoint = null;
+
+ if (selrect.isSet() && selrect.selectCornerAt(x, y)) {
+ point = e.getPoint();
+ oldrect = selrect.getRectangle();
+ view.repaint();
+ return;
+ }
+ oldrect = null;
+ point = null;
+
+ TreePath path = view.getPathToNodeAt(x, y);
+ if (path == null) {
+ return;
+ }
+ view.fireTreeSelection(path);
+ //System.out.println("x: " + e.getX() + ", y: " + e.getY() + ", path: " + path);
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+
+ SelectionRectangle selrect = view.getSelectionRectangle();
+ if (e.isPopupTrigger()) {
+ objectViewPopup.showPopup(e);
+ selrect.deselectCorners();
+ return;
+ }
+ if (point == null) {
+ return;
+ }
+
+ int x = e.getX();
+ int y = e.getY();
+ double z = view.getZoom();
+
+ // polygon corner
+ if (oldpoint != null) {
+ selrect.moveSelectedPolyCorner(x - point.x, y - point.y);
+ Element element = selrect.getSelectedPolyCorner().actual();
+ if (element != null) {
+ changeAttribute(element, POS_X, x - oldpoint.x, z);
+ changeAttribute(element, POS_Y, y - oldpoint.y, z);
+ }
+ }
+ // normal corner
+ if (oldrect != null) {
+ selrect.moveSelectedCorner(x - point.x, y - point.y);
+ Rectangle newrect = selrect.getRectangle();
+ XMLTreeNode node = (XMLTreeNode) selrect.getSource();
+
+ int dx = newrect.x - oldrect.x;
+ int dy = newrect.y - oldrect.y;
+ Element pos = node.link() != null ? node.link() : node.actual();
+ if (pos != null) {
+ changeAttribute(pos, POS_X, dx, z);
+ changeAttribute(pos, POS_Y, dy, z);
+ }
+
+ int dw = newrect.width - oldrect.width;
+ int dh = newrect.height - oldrect.height;
+ Element size = node.actual();
+ if (size != null) {
+ changeAttribute(size, WIDTH, dw, z);
+ changeAttribute(size, HEIGHT, dh, z);
+ }
+
+ if (smartMoves) {
+ // if object is expanded left, move children right to
+ // keep them stationary
+ if (dx == -dw) {
+ NodeList elements = size.getChildNodes();
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Node child = elements.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ changeAttribute((Element) child, POS_X, dw, z);
+ }
+ }
+ }
+
+ // if object is expanded up, move children down to
+ // keep them stationary
+ if (dy == -dh) {
+ NodeList elements = size.getChildNodes();
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Node child = elements.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ changeAttribute((Element) child, POS_Y, dh, z);
+ }
+ }
+ }
+ }
+ }
+ selrect.deselectCorners();
+ view.repaint();
+
+ // just in case...
+ point = null;
+ oldrect = null;
+ oldpoint = null;
+ }
+
+ /**
+ * Changes the specified position or size attribute of the given element.
+ * The attribute is changed by difference of the new and old pixel
+ * coordinates. Zoom factor is used to transform pixel coordinates to
+ * actual design coordinates.
+ *
+ * If the specified attribute does not exist, this method does nothing.
+ *
+ * @param element
+ * @param attr
+ * @param diff
+ * @param zoom
+ */
+ private void changeAttribute(Element element, String attr, int diff, double zoom) {
+ String val = element.getAttribute(attr);
+ if (!val.isEmpty()) {
+ int p = Integer.parseInt(val);
+ int d = (int) (diff / zoom);
+ if (d != 0) {
+ element.setAttribute(attr, Integer.toString(p + d));
+ }
+ }
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ }
+
+ /**
+ * Implements zooming with the mouse wheel.
+ * TODO: use the pointer position to affect the zooming area?
+ * @param e
+ */
+ @Override
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ int r = e.getWheelRotation();
+ double z = view.getZoom();
+ double z_ = z + 0.5 * r;
+ z_ = Math.min(z_, MAX_ZOOM_FACTOR);
+ z_ = Math.max(z_, MIN_ZOOM_FACTOR);
+
+ // adjust the viewport (if any) so that the point under the mouse
+ // pointer stays stationary (in design coordinates)
+ Container parent = view.getParent();
+ if (parent instanceof JViewport) {
+ double k = (z_ / z) - 1;
+ int xm = e.getX();
+ int ym = e.getY();
+ JViewport viewport = (JViewport) parent;
+ Point p = viewport.getViewPosition();
+ double x = k * xm + p.x;
+ double y = k * ym + p.y;
+ viewport.setViewPosition(new Point((int) (x + 0.5), (int) (y + 0.5)));
+ }
+
+ view.setZoom(z_);
+ }
+}
diff --git a/src/objectview/ObjectOperations.java b/src/objectview/ObjectOperations.java
new file mode 100644
index 0000000..a2c5ff8
--- /dev/null
+++ b/src/objectview/ObjectOperations.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objectview;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public interface ObjectOperations {
+ void setDepth(int depth);
+
+ void incDepth();
+
+ void decDepth();
+
+ int getDepth();
+
+ void opAlarmMask(Graphics2D gfx, XMLTreeNode node, int w, int h);
+
+ void opArchedBarGraph(Graphics2D gfx, XMLTreeNode node);
+
+ void opAuxiliaryFunction(Graphics2D gfx, XMLTreeNode node, int w, int h);
+
+ void opAuxiliaryInput(Graphics2D gfx, XMLTreeNode node, int w, int h);
+
+ void opButton(Graphics2D gfx, XMLTreeNode node);
+
+ void opContainer(Graphics2D gfx, XMLTreeNode node);
+
+ void opDataMask(Graphics2D gfx, XMLTreeNode node, int w, int h);
+
+ void opEllipse(Graphics2D gfx, XMLTreeNode node, String imagepath) throws IOException;
+
+ void opInputBoolean(Graphics2D gfx, XMLTreeNode node);
+
+ void opInputList(Graphics2D gfx, XMLTreeNode node);
+
+ void opInputNumber(Graphics2D gfx, XMLTreeNode node) throws IOException;
+
+ void opInputString(Graphics2D gfx, XMLTreeNode node) throws IOException;
+
+ void opKey(Graphics2D gfx, XMLTreeNode node, int w, int h);
+
+ void opLine(Graphics2D gfx, XMLTreeNode node);
+
+ void opLinearBarGraph(Graphics2D gfx, XMLTreeNode node);
+
+ void opMeter(Graphics2D gfx, XMLTreeNode node);
+
+ void opObjectPointer(Graphics2D gfx, XMLTreeNode node);
+
+ void opOutputNumber(Graphics2D gfx, XMLTreeNode node) throws IOException;
+
+ void opOutputString(Graphics2D gfx, XMLTreeNode node) throws IOException;
+
+ void opPictureGraphic(Graphics2D gfx, XMLTreeNode node, BufferedImage image);
+
+ void opPolygon(Graphics2D gfx, XMLTreeNode node, String imagepath) throws IOException;
+
+ void opRectangle(Graphics2D gfx, XMLTreeNode node, String imagepath) throws IOException;
+
+ void opSoftKeyMask(Graphics2D gfx, XMLTreeNode node, int w, int h, int sk_height);
+
+ void opWorkingSet(Graphics2D gfx, XMLTreeNode node, int w, int h);
+}
diff --git a/src/objectview/ObjectView.java b/src/objectview/ObjectView.java
new file mode 100644
index 0000000..baa5fff
--- /dev/null
+++ b/src/objectview/ObjectView.java
@@ -0,0 +1,1518 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objectview;
+
+import static pooledit.Definitions.*;
+import org.w3c.dom.Element;
+import java.io.PrintStream;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.util.Vector;
+import javax.swing.SwingUtilities;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.TexturePaint;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.*;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.tree.TreePath;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.util.Timer;
+import java.util.TimerTask;
+import javax.swing.JComponent;
+import javax.swing.Scrollable;
+import javax.swing.event.TreeModelListener;
+import pooledit.Utils;
+import treemodel.XMLTreeModel;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class ObjectView extends JComponent implements TreeModelListener,
+ Scrollable, KeyListener {
+
+ private final boolean DEBUG = false;
+
+ /**
+ * Padding around the graphical presentation of the selected path,
+ * THIS MUST BE GREATER THAN ZERO!
+ */
+ static private final int EXTRA_SPACE = 8;
+ static private final int DEPTH_LIMIT = 20;
+
+ //static private final AffineTransform IDENTITY = new AffineTransform();
+ static private final GraphicObjectOperations GFXOP = new GraphicObjectOperations();
+
+ static private int IMAGE_FULL = 16;
+ static private int IMAGE_HALF = IMAGE_FULL / 2;
+ static private Paint BACKGROUND = createPaint();
+ static private Paint createPaint() {
+ BufferedImage image = new BufferedImage(IMAGE_FULL, IMAGE_FULL,
+ BufferedImage.TYPE_INT_RGB);
+ Graphics2D gfx = image.createGraphics();
+ gfx.setColor(Color.WHITE);
+ gfx.fillRect(0, 0, IMAGE_FULL, IMAGE_FULL);
+ gfx.setColor(Color.LIGHT_GRAY);
+ gfx.fillRect(0, 0, IMAGE_HALF, IMAGE_HALF);
+ gfx.fillRect(IMAGE_HALF, IMAGE_HALF, IMAGE_HALF, IMAGE_HALF);
+ gfx.dispose();
+ Rectangle anchor = new Rectangle(0, 0, IMAGE_FULL, IMAGE_FULL);
+ return new TexturePaint(image, anchor);
+ }
+
+ private final HelpGrid grid = new HelpGrid();
+ private final SelectionRectangle selrect = new SelectionRectangle();
+ private final MouseController mc = new MouseController(this, new ObjectViewPopup(this));
+
+ private PrintStream out;
+ private XMLTreeModel model;
+ private TreePath path;
+ private boolean newModel = true;
+
+ private double zoom = 1.0;
+
+ // root node properties are cached here for convenient access
+ private int dimension;
+ private int sk_width;
+ private int sk_height;
+ private String fix_bitmap_path;
+ private String std_bitmap_path;
+
+ /** Image for double buffering */
+ private Image bff;
+
+ private boolean drawGrid = false;
+ private boolean imageZoom = false;
+
+ private Timer flashTimer;
+
+ class Lim1D {
+ int min, max, val;
+ void move(int v) {
+ update(val += v);
+ }
+ void set(int s) {
+ update(val + s);
+ }
+ private void update(int p) {
+ if (p < min) { min = p; }
+ else if (p > max) { max = p; }
+ }
+ }
+
+ class Lim2D {
+ Lim1D lx = new Lim1D();
+ Lim1D ly = new Lim1D();
+ void move(int x, int y) {
+ lx.move(x); ly.move(y);
+ }
+ void set(int w, int h) {
+ lx.set(w); ly.set(h);
+ }
+ boolean setAndTest(int w, int h, Point p) {
+ lx.set(w); ly.set(h);
+ return (p == null) ? false : (0 <= p.x && p.x < w && 0 <= p.y && p.y < h);
+ }
+ int getMaxX() {
+ return lx.max;
+ }
+ int getMaxY() {
+ return ly.max;
+ }
+ }
+
+ /**
+ * Default constructor
+ */
+ public ObjectView() {
+ this(null, System.out);
+ }
+
+ /**
+ * Constructor.
+ * @param out
+ */
+ public ObjectView(PrintStream out) {
+ this(null, out);
+ }
+
+ /**
+ * Constructor.
+ * @param model
+ * @param out
+ */
+ public ObjectView(XMLTreeModel model, PrintStream out) {
+ // double buffering is implemented in own code
+ this.setDoubleBuffered(false);
+
+ // set key listener
+ this.setFocusable(true);
+ this.addKeyListener(this);
+
+ // set mouse listeners
+ this.addMouseListener(mc);
+ this.addMouseMotionListener(mc);
+ this.addMouseWheelListener(mc);
+
+ // set model
+ this.setMessageStream(out);
+ this.setModel(model);
+ }
+
+ /**
+ * Prints debug messages to System.out.
+ * @param obj
+ */
+ private void dmsg(Object obj) {
+ if (DEBUG) {
+ System.out.println(obj);
+ }
+ }
+
+ /**
+ * Sets a new model to this view. This method will have no effect if
+ * the new model is the same as the current model.
+ * @param model
+ */
+ public void setModel(XMLTreeModel model) {
+ if (this.model == model) {
+ return;
+ }
+ // set model
+ if (this.model != null) {
+ this.model.removeTreeModelListener(this);
+ }
+ this.model = model;
+ this.newModel = true;
+ this.model.addTreeModelListener(this);
+ repaint();
+ }
+
+ /**
+ * Sets the stream where warning and error messages are printed.
+ * @param out
+ */
+ public void setMessageStream(PrintStream out) {
+ this.out = out;
+ }
+
+ /**
+ * Returns the current model.
+ * @return
+ */
+ public XMLTreeModel getModel() {
+ return model;
+ }
+
+ /**
+ * Sets a new active path. This method will have no effect if the new
+ * path is the same as the current path.
+ * @param path
+ */
+ public void setActivePath(TreePath path) {
+ if (Utils.equalObjects(this.path, path) && !newModel) {
+ return;
+ }
+ this.path = path;
+ this.newModel = false;
+ // System.out.println(getClass().getName() + ": setActivePath(): " + path);
+ // try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); }
+ repaint();
+ }
+
+ /**
+ * Gets the active path.
+ * @return
+ */
+ public TreePath getActivePath() {
+ return path;
+ }
+
+ /**
+ * Sets the zoom factor.
+ * @param zoom
+ */
+ public void setZoom(double zoom) {
+ this.zoom = zoom;
+ repaint();
+ }
+
+ /**
+ * Gets the zoom factor.
+ * @return
+ */
+ public double getZoom() {
+ return this.zoom;
+ }
+
+ /**
+ * Sets the draw borders flag.
+ * @param drawBorders
+ */
+ public void setDrawBorders(boolean drawBorders) {
+ GFXOP.setDrawBorders(drawBorders);
+ repaint();
+ }
+
+ /**
+ * Gets the draw borders flag.
+ * @return
+ */
+ public boolean getDrawBorders() {
+ return GFXOP.getDrawBorders();
+ }
+
+ /**
+ * Sets the color depth.
+ * @param colorDepth
+ */
+ public void setColorDepth(int colorDepth) {
+ GFXOP.setColorDepth(colorDepth);
+ }
+
+ /**
+ * Gets the color depth.
+ * @return
+ */
+ public int getColorDepth() {
+ return GFXOP.getColorDepth();
+ }
+
+ /**
+ * Sets the reduce images flag.
+ * @param reduceImages
+ */
+ public void setReduceImages(boolean reduceImages) {
+ GFXOP.setReduceImages(reduceImages);
+ repaint();
+ }
+
+ /**
+ * Gets the reduce images flag.
+ * @return
+ */
+ public boolean getReduceImages() {
+ return GFXOP.getReduceImages();
+ }
+
+ /**
+ * Sets the flashing feature on and off. When flashing is
+ * enabled a new timer (thread) is created. When flasing is
+ * disabled this timer is stopped. Flashing effect is created
+ * by periodically toggling the flash attribute in the graphics
+ * object.
+ * @param flashing
+ */
+ public void setFlashing(boolean flashing) {
+ if (flashing) {
+ flashTimer = new Timer("flashTimer");
+ flashTimer.scheduleAtFixedRate(new TimerTask() {
+ boolean flash;
+ @Override
+ public void run() {
+ GFXOP.setFlash(flash);
+ flash = !flash;
+ repaint();
+ }
+ }, 500, 500);
+ }
+ else {
+ flashTimer.cancel();
+ flashTimer = null;
+ GFXOP.setFlash(false);
+ }
+ repaint();
+ }
+
+ /**
+ * Gets the state of the flashing feature.
+ * @return
+ */
+ public boolean getFlashing() {
+ return flashTimer != null;
+ }
+
+ /**
+ * Sets the draw grid flag.
+ * @param drawGrid
+ */
+ public void setDrawGrid(boolean drawGrid) {
+ this.drawGrid = drawGrid;
+ repaint();
+ }
+
+ /**
+ * Gets the draw grid flag.
+ * @return
+ */
+ public boolean getDrawGrid() {
+ return this.drawGrid;
+ }
+
+ /**
+ * Sets the image zoom flag.
+ * @param imageZoom
+ */
+ public void setImageZoom(boolean imageZoom) {
+ this.imageZoom = imageZoom;
+ repaint();
+ }
+
+ /**
+ * Gets the image zoom flag.
+ * @return
+ */
+ public boolean getImageZoom() {
+ return this.imageZoom;
+ }
+
+ /**
+ * Gets the selection rectangle.
+ * @return
+ */
+ public SelectionRectangle getSelectionRectangle() {
+ return this.selrect;
+ }
+
+ /**
+ * Gets the path to the node at the specified coordinates.
+ * @param x
+ * @param y
+ * @return
+ */
+ public TreePath getPathToNodeAt(int x, int y) {
+ XMLTreeNode start = getStartNode();
+ if (start == null) {
+ return null;
+ }
+ Graphics2D gfx = (Graphics2D) this.getGraphics();
+ gfx.scale(zoom, zoom);
+ gfx.translate(EXTRA_SPACE / 2, EXTRA_SPACE / 2);
+ PointObjectOperations op = new PointObjectOperations(x, y);
+ processNode(gfx, start, op);
+ TreePath pointedPath = op.getPointedPath();
+ if (pointedPath == null) {
+ return null;
+ }
+ Object[] m = mergeArrays(path.getPath(), op.getPointedPath().getPath());
+ return new TreePath(m);
+ }
+
+ /**
+ * Merges two arrays.
+ * @param start
+ * @param end
+ * @return
+ */
+ static private Object[] mergeArrays(Object[] start, Object[] end) {
+ for (int i = 0, n = start.length; i < n; i++) {
+ if (start[i].equals(end[0])) {
+ Object[] merge = new Object[i + end.length];
+ System.arraycopy(start, 0, merge, 0, i);
+ System.arraycopy(end, 0, merge, i, end.length);
+ return merge;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Regenerates path. Will cause firing of a tree selection event.
+ */
+ private void regeneratePath() {
+ if (path == null) {
+ return;
+ }
+ Object[] oldpath = path.getPath();
+ TreePath newpath = new TreePath(model.getRoot());
+ boolean loop = true;
+ for (int j = 1, m = oldpath.length; j < m; j++) {
+ loop = false;
+ XMLTreeNode node = (XMLTreeNode) newpath.getLastPathComponent();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode newnode = (XMLTreeNode) model.getChild(node, i);
+ XMLTreeNode oldnode = (XMLTreeNode) oldpath[j];
+ if (newnode == oldnode) {
+ newpath = newpath.pathByAddingChild(newnode);
+ loop = true;
+ break;
+ }
+ }
+ }
+ this.fireTreeSelection(newpath);
+ }
+
+ /**
+ * Gets the node where the rendering should start.
+ * @return
+ */
+ public XMLTreeNode getStartNode() {
+ // nothing to select
+ if (path == null || model == null || model.getRoot() == null) {
+ return null;
+ }
+ // no proper object selected
+ Object[] p = path.getPath();
+ XMLTreeNode node = (XMLTreeNode) p[p.length - 1];
+ if (!node.isType(OBJECTS) && !node.isType(POINT) && !node.isType(FIXEDBITMAP)) {
+ return null;
+ }
+ // find last drawable node
+ for (int i = p.length - 2; i >= 0; i--) {
+ node = (XMLTreeNode) p[i];
+ if (!node.isType(OBJECTS) && !node.isType(POINT)) {
+ GFXOP.setDepth(i + 1);
+ return (XMLTreeNode) p[i + 1];
+ }
+ }
+ GFXOP.setDepth(0);
+ return (XMLTreeNode) p[0];
+ }
+
+ /**
+ * Paints this component.
+ * @param g
+ */
+ @Override
+ public void paintComponent(Graphics g) {
+ dmsg("paintComponent");
+ Graphics2D gfx = (Graphics2D) g;
+
+ // FIXME: this is test code to get something meaningful to
+ // draw, real code should use current selected path to find
+ // out the proper starting point
+ XMLTreeNode start = getStartNode();
+ if (start == null) {
+ return;
+ }
+
+ XMLTreeNode root = (XMLTreeNode) model.getRoot();
+ dimension = root.getDimension();
+ sk_width = root.getSKWidth();
+ sk_height = root.getSKHeight();
+
+ fix_bitmap_path = root.getFixBitmapPath();
+ std_bitmap_path = root.getStdBitmapPath();
+
+ // FIXME: is there a better place for this code? Size cannot
+ // be calculated in setzoom because the result depends on the
+ // model and active node
+ Lim2D lim = new Lim2D();
+ calcSize(lim, start);
+
+ // nasty things happen if the preferred size is set to zero
+ int w = lim.getMaxX() + EXTRA_SPACE;
+ int h = lim.getMaxY() + EXTRA_SPACE;
+
+ // this should not be this complicated, but it is...
+ if (zoom > 1 || !imageZoom) {
+ w *= zoom;
+ h *= zoom;
+ }
+
+ Dimension prefSize = getPreferredSize();
+ if (prefSize.width != w || prefSize.height != h) {
+ setPreferredSize(new Dimension(w, h));
+ bff = createImage(w, h);
+ revalidate(); // this will cause a new call to paintComponent
+ return;
+ }
+
+ grid.reset();
+ selrect.reset();
+
+ AffineTransform oldx = gfx.getTransform();
+ Graphics2D img = (Graphics2D) bff.getGraphics();
+ img.setPaint(BACKGROUND);
+ img.fillRect(0, 0, w, h);
+
+ // paint to buffer and then paint the buffer to the screen
+ if (imageZoom) {
+ img.translate(EXTRA_SPACE / 2, EXTRA_SPACE / 2);
+ processNode(img, start, GFXOP);
+ gfx.scale(zoom, zoom);
+ }
+ // paint "directly" to screen
+ else {
+ img.scale(zoom, zoom);
+ img.translate(EXTRA_SPACE / 2, EXTRA_SPACE / 2);
+ processNode(img, start, GFXOP);
+ }
+ gfx.drawImage(bff, 0, 0, this);
+
+ // selection is defined in absolute image coordinates, but it is drawn
+ // in "mouse coordinates" (relative to the upper left corner of the
+ // parent component -> coordinate transform is needed
+ try {
+ AffineTransform tmp = oldx.createInverse();
+ tmp.concatenate(gfx.getTransform());
+ grid.adjust(tmp);
+ selrect.adjust(tmp);
+ } catch (NoninvertibleTransformException ex) {
+ ex.printStackTrace(); // this should never happen!
+ }
+
+ // selection painted in "mouse coordinates" (in original graphics
+ // coordinates)
+ gfx.setTransform(oldx);
+ if (grid.isSet()) {
+ grid.draw(gfx, zoom);
+ }
+ if (selrect.isSet()) {
+ selrect.draw(gfx, zoom);
+ }
+ }
+
+ /**
+ * Painting dispatch method.
+ * @param gfx
+ * @param node
+ * @param oper
+ * @return
+ */
+ public Shape processNode(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ if (oper.getDepth() > DEPTH_LIMIT) {
+ out.println("DRAWING DEPTH LIMIT EXCEEDED!");
+ return null;
+ }
+
+ Shape shape = null;
+ String type = node.getType();
+ try {
+ if (type == null) {
+ // normally this should not happen
+ }
+ else if (type.equals(WORKINGSET)) {
+ shape = processWorkingSet(gfx, node, oper);
+ }
+ else if (type.equals(DATAMASK)) {
+ shape = processDataMask(gfx, node, oper);
+ }
+ else if (type.equals(ALARMMASK)) {
+ shape = processAlarmMask(gfx, node, oper);
+ }
+ else if (type.equals(CONTAINER)) {
+ shape = processContainer(gfx, node, oper);
+ }
+ else if (type.equals(SOFTKEYMASK)) {
+ shape = processSoftKeyMask(gfx, node, oper);
+ }
+ else if (type.equals(KEY)) {
+ shape = processKey(gfx, node, oper);
+ }
+ else if (type.equals(BUTTON)) {
+ shape = processButton(gfx, node, oper);
+ }
+ else if (type.equals(INPUTBOOLEAN)) {
+ shape = processInputboolean(gfx, node, oper);
+ }
+ else if (type.equals(INPUTSTRING)) {
+ shape = processInputString(gfx, node, oper);
+ }
+ else if (type.equals(INPUTNUMBER)) {
+ shape = processInputNumber(gfx, node, oper);
+ }
+ else if (type.equals(INPUTLIST)) {
+ shape = processInputList(gfx, node, oper);
+ }
+ else if (type.equals(OUTPUTSTRING)) {
+ shape = processOutputString(gfx, node, oper);
+ }
+ else if (type.equals(OUTPUTNUMBER)) {
+ shape = processOutputNumber(gfx, node, oper);
+ }
+ else if (type.equals(LINE)) {
+ shape = processLine(gfx, node, oper);
+ }
+ else if (type.equals(RECTANGLE)) {
+ shape = processRectangle(gfx, node, oper);
+ }
+ else if (type.equals(ELLIPSE)) {
+ shape = processEllipse(gfx, node, oper);
+ }
+ else if (type.equals(POLYGON)) {
+ shape = processPolygon(gfx, node, oper);
+ }
+ else if (type.equals(METER)) {
+ shape = processMeter(gfx, node, oper);
+ }
+ else if (type.equals(LINEARBARGRAPH)) {
+ shape = processLinearBarGraph(gfx, node, oper);
+ }
+ else if (type.equals(ARCHEDBARGRAPH)) {
+ shape = processArchedBarGraph(gfx, node, oper);
+ }
+ else if (type.equals(PICTUREGRAPHIC)) {
+ shape = processPictureGraphic(gfx, node, oper);
+ }
+ else if (type.equals(OBJECTPOINTER)) {
+ shape = processObjectPointer(gfx, node, oper);
+ }
+ else if (type.equals(AUXILIARYFUNCTION)) {
+ shape = processAuxiliaryFunction(gfx, node, oper);
+ }
+ else if (type.equals(AUXILIARYINPUT)) {
+ shape = processAuxiliaryInput(gfx, node, oper);
+ }
+ else {
+ // node is not drawable, do nothing
+ }
+ }
+ catch (Exception ex) {
+ System.err.println("### " + node.getType() + ": " + node.getName() + " ###");
+ ex.printStackTrace();
+ }
+ /*
+ // the desired node is the last one matching the given point, however
+ // this code is run when the recursion is already unfolding
+ // WILL NOT WORK CORRECTLY! THE EVALUATION ORDER IS NOT 100% CORRECT
+ if (shape != null && pointedNode == null &&
+ gfx.getTransform().createTransformedShape(shape).contains(point)) {
+
+ pointedNode = node;
+ }
+ */
+
+ // if the node is drawable (i.e. shape exists) and it is the
+ // selected node, remember its position
+
+ if (shape != null) {
+ int nro = path.getPathCount();
+ AffineTransform trans = gfx.getTransform();
+ if (nro > 0) {
+ XMLTreeNode last = (XMLTreeNode) path.getPathComponent(nro - 1);
+ if (last.equals(node)) {
+ // last path component matches node
+ selrect.set(trans, shape, node);
+ }
+ else if (drawGrid && nro > 1) {
+ XMLTreeNode secondLast = (XMLTreeNode) path.getPathComponent(nro - 2);
+
+ if (secondLast.equals(node) &&
+ !Utils.equals(last.getType(), WORKINGSET, DATAMASK, ALARMMASK, SOFTKEYMASK, KEY)) {
+ // second last path component matches node
+ grid.set(trans, shape, node);
+ }
+ }
+ }
+ }
+ return shape;
+ }
+
+ /**
+ * A helper method for changing the clip.
+ * @param gfx
+ * @param width
+ * @param height
+ * @return
+ */
+ static private Shape changeClip(Graphics2D gfx, int width, int height) {
+ Shape oldc = gfx.getClip();
+ gfx.clipRect(0, 0, width, height);
+ return oldc;
+ }
+
+ /**
+ * a helper method for restoring the clip. This method works exactly the
+ * same as changeClip method, but it uses different name (instead of
+ * overloading) to make the code more readable.
+ * @param gfx
+ * @param clip
+ * @return
+ */
+ static private Shape restoreClip(Graphics2D gfx, Shape clip) {
+ Shape oldc = gfx.getClip();
+ gfx.setClip(clip);
+ return oldc;
+ }
+
+ public Shape processWorkingSet(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintWorkingSet");
+ Shape oldc = changeClip(gfx, sk_width, sk_height);
+
+ boolean selectable = node.isSelectable();
+
+ oper.opWorkingSet(gfx, node, sk_width, sk_height);
+
+ oper.incDepth();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+
+ // active_mask is a special child
+ if (nd.isType(DATAMASK, ALARMMASK)) {
+ if (selectable && nd.getRole().equals(ACTIVE_MASK)) {
+ // draw mask below the working set designator
+ gfx.setClip(oldc);
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(0, sk_height);
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ gfx.clipRect(0, 0, sk_width, sk_height);
+ }
+ }
+ else if (nd.isType(LANGUAGE)) {
+ // do nothing?
+ }
+ else if (nd.isType(MACRO)) {
+ // do nothing?
+ }
+ // this could be more specific, the main idea is to prevent
+ // processing of bogus nodes such as broken links
+ else if (nd.isType(OBJECTS)) {
+ // paint children
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(nd.getX(), nd.getY());
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ }
+ }
+ oper.decDepth();
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processDataMask(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintDataMask");
+ Shape oldc = changeClip(gfx, dimension, dimension);
+
+ oper.opDataMask(gfx, node, dimension, dimension);
+
+ oper.incDepth();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+
+ // soft_key_mask is a special child
+ if (nd.isType(SOFTKEYMASK)) {
+ // draw softkeymask on the right side of the mask
+ gfx.setClip(oldc);
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(dimension, 0);
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ gfx.clipRect(0, 0, dimension - 1, dimension - 1);
+ }
+ else if (nd.isType(MACRO)) {
+ // do nothing?
+ }
+ // this could be more specific
+ else if (nd.isType(OBJECTS)) {
+ // paint children
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(nd.getX(), nd.getY());
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ }
+ }
+ oper.decDepth();
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processAlarmMask(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintAlarmMask");
+
+ Shape oldc = changeClip(gfx, dimension, dimension);
+
+ oper.opAlarmMask(gfx, node, dimension, dimension);
+
+ oper.incDepth();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+
+ // soft_key_mask is a special child
+ if (nd.isType(SOFTKEYMASK)) {
+ // draw softkeymask on the right side of the mask
+ gfx.setClip(oldc);
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(dimension, 0);
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ gfx.clipRect(0, 0, dimension - 1, dimension - 1);
+ }
+ else if (nd.isType(MACRO)) {
+ // do nothing?
+ }
+ // this could be more specific
+ else if (nd.isType(OBJECTS)) {
+ // paint children
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(nd.getX(), nd.getY());
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ }
+ }
+ oper.decDepth();
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processContainer(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintContainer");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ oper.opContainer(gfx, node);
+
+ if (!node.isHidden()) {
+ // draw children
+ oper.incDepth();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (nd.isType(MACRO)) {
+ // do nothing?
+ }
+ // this could be more specific
+ else if (nd.isType(OBJECTS)) {
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(nd.getX(), nd.getY());
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ }
+ }
+ oper.decDepth();
+ }
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processSoftKeyMask(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintSoftKeyMask");
+ int w = sk_width;
+ int h = (model.getChildCount(node) + 1) * sk_height;
+ Shape oldc = changeClip(gfx, w, h);
+
+ oper.opSoftKeyMask(gfx, node, w, h, sk_height);
+
+ oper.incDepth();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (nd.isType(MACRO)) {
+ // do nothing?
+ }
+ // these should all be softkeys
+ else if (nd.isType(OBJECTS)) {
+ // draw each softkey below the previous softkey
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(0, sk_height * i);
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ }
+ }
+ oper.decDepth();
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processKey(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintKey");
+ Shape oldc = changeClip(gfx, sk_width, sk_height);
+
+ oper.opKey(gfx, node, sk_width, sk_height);
+
+ // paint children
+ oper.incDepth();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (nd.isType(MACRO)) {
+ // do nothing?
+ }
+ // this could be more specific
+ else if (nd.isType(OBJECTS)) {
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(nd.getX(), nd.getY());
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ }
+ }
+ oper.decDepth();
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processButton(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintButton");
+ int w = node.getWidth();
+ int h = node.getHeight();
+ Shape oldc = changeClip(gfx, w, h);
+
+ oper.opButton(gfx, node);
+
+ // paint children
+ oper.incDepth();
+ Shape oldc2 = gfx.getClip();
+ gfx.clipRect(4, 4, w - 8, h - 8); // FIXME: magic numbers (are these from the standard?)
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (nd.isType(MACRO)) {
+ // do nothing?
+ }
+ // this could be more specific
+ else if (nd.isType(OBJECTS)) {
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(nd.getX() + 4, nd.getY() + 4);
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ }
+ }
+ gfx.setClip(oldc2);
+ oper.decDepth();
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processInputboolean(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintInputboolean");
+
+ int w = node.getWidth();
+ Shape oldc = changeClip(gfx, w, w); // width == height
+
+ oper.opInputBoolean(gfx, node);
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processInputString(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintInputString");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ try {
+ oper.opInputString(gfx, node);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processInputNumber(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintInputNumber: " + node.getName());
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ try {
+ oper.opInputNumber(gfx, node);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processInputList(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintInputList");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ oper.opInputList(gfx, node);
+
+ // draw the child in the path
+ oper.incDepth();
+ if (oper.getDepth() < path.getPathCount()) {
+ XMLTreeNode pathnode = (XMLTreeNode) path.getPathComponent(oper.getDepth());
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (pathnode == nd) {
+ processNode(gfx, nd, oper);
+
+ oper.decDepth();
+ return restoreClip(gfx, oldc);
+ }
+ }
+ }
+
+ // draw selected child
+ int val = node.getValueInt();
+ if (val < model.getChildCount(node)) {
+ processNode(gfx, (XMLTreeNode) model.getChild(node, val), oper);
+ }
+
+ oper.decDepth();
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processOutputString(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintOutputString");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ try {
+ oper.opOutputString(gfx, node);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processOutputNumber(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintOutputNumber");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ try {
+ oper.opOutputNumber(gfx, node);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processLine(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintLine");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ oper.opLine(gfx, node);
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processRectangle(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintRectangle");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ try {
+ oper.opRectangle(gfx, node, std_bitmap_path);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processEllipse(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintEllipse");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ try {
+ oper.opEllipse(gfx, node, std_bitmap_path);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+
+ return restoreClip(gfx, oldc);
+ }
+
+ // There is a problem with drawing thick lines if they don't fit inside the
+ // clipping rectangle.
+ public Shape processPolygon(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintPolygon");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ try {
+ oper.opPolygon(gfx, node, std_bitmap_path);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processMeter(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintMeter");
+ int w = node.getWidth();
+ Shape oldc = changeClip(gfx, w, w);
+
+ oper.opMeter(gfx, node);
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processLinearBarGraph(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintLinearBarGraph");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ oper.opLinearBarGraph(gfx, node);
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processArchedBarGraph(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintArchedBarGraph");
+ Shape oldc = changeClip(gfx, node.getWidth(), node.getHeight());
+
+ oper.opArchedBarGraph(gfx, node);
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processPictureGraphic(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintPictureGraphic");
+ BufferedImage image;
+ try {
+ image = node.getImageFile();
+ if (image == null) {
+ return null;
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ int w = node.getWidth();
+ int h = w * image.getHeight() / image.getWidth();
+
+ Shape oldc = changeClip(gfx, w, h);
+
+ oper.opPictureGraphic(gfx, node, image);
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processObjectPointer(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintObjectPointer");
+
+ // should be only one?
+ oper.incDepth();
+ Shape shape = null;
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ shape = processNode(gfx, (XMLTreeNode) model.getChild(node, i), oper);
+ }
+ oper.decDepth();
+
+ // this is a nasty special case - object pointer is processed after
+ // its children
+ Shape oldc = gfx.getClip();
+ gfx.setClip(shape);
+ oper.opObjectPointer(gfx, node);
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processAuxiliaryFunction(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintAuxiliaryFunction");
+ Shape oldc = changeClip(gfx, sk_width, sk_height);
+
+ oper.opAuxiliaryFunction(gfx, node, sk_width, sk_height);
+
+ // paint children
+ oper.incDepth();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (nd.isType(MACRO)) {
+ // do nothing?
+ }
+ // this could be more specific
+ else if (nd.isType(OBJECTS)) {
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(nd.getX(), nd.getY());
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ }
+ }
+ oper.decDepth();
+
+ return restoreClip(gfx, oldc);
+ }
+
+ public Shape processAuxiliaryInput(Graphics2D gfx, XMLTreeNode node, ObjectOperations oper) {
+ dmsg("paintAuxiliaryInput");
+ Shape oldc = changeClip(gfx, sk_width, sk_height);
+
+ oper.opAuxiliaryInput(gfx, node, sk_width, sk_height);
+
+ // paint children
+ oper.incDepth();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (nd.isType(MACRO)) {
+ // do nothing?
+ }
+ // this could be more specific
+ else if (nd.isType(OBJECTS)) {
+ AffineTransform oldx = gfx.getTransform();
+ gfx.translate(nd.getX(), nd.getY());
+ processNode(gfx, nd, oper);
+ gfx.setTransform(oldx);
+ }
+ }
+ oper.decDepth();
+
+ return restoreClip(gfx, oldc);
+ }
+
+ /**
+ * Calculates required size by using a very limited recursion.
+ * @param lim
+ * @param node
+ */
+ void calcSize(Lim2D lim, XMLTreeNode node) {
+ if (node.isType(WORKINGSET)) {
+ lim.set(sk_width, sk_height);
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (nd.isType(DATAMASK, ALARMMASK)) {
+ lim.move(0, sk_height);
+ calcSize(lim, nd);
+ lim.move(0, -sk_height);
+ }
+ }
+ }
+ else if (node.isType(DATAMASK, ALARMMASK)) {
+ lim.set(dimension, dimension);
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (nd.isType(SOFTKEYMASK)) {
+ lim.move(dimension, 0);
+ calcSize(lim, nd);
+ lim.move(0, -sk_height);
+ }
+ }
+ }
+ else if (node.isType(SOFTKEYMASK)) {
+ // reserve roperm for one extra key
+ lim.set(sk_width, (model.getChildCount(node) + 1) * sk_height);
+ }
+ else if (node.isType(KEY, AUXILIARYFUNCTION, AUXILIARYINPUT)) {
+ lim.set(sk_width, sk_height);
+ }
+ else if (node.isType(CONTAINER, BUTTON,
+ INPUTSTRING, INPUTNUMBER, INPUTLIST,
+ OUTPUTSTRING, OUTPUTNUMBER,
+ LINE, RECTANGLE, ELLIPSE, POLYGON,
+ LINEARBARGRAPH, ARCHEDBARGRAPH)) {
+ lim.set(node.getWidth(), node.getHeight());
+ }
+ else if (node.isType(INPUTBOOLEAN, METER)) {
+ int w = node.getWidth();
+ lim.set(w, w);
+ }
+ else if (node.isType(PICTUREGRAPHIC)) {
+ try {
+ BufferedImage image = node.getImageFile(); // for size calculation only
+ if(image != null) {
+ int w = node.getWidth();
+ int h = w * image.getHeight() / image.getWidth();
+ lim.set(w, h);
+ }
+ }
+ catch (IOException ex) {
+ ex.printStackTrace();
+ lim.set(64, 64); // questimate picture size?
+ }
+ }
+ else if (node.isType(OBJECTPOINTER)) {
+ // should be only one
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ calcSize(lim, nd);
+ }
+ }
+ }
+
+ //------------------------------------------------------------//
+
+ private final Vector listeners =
+ new Vector();
+
+ /**
+ * Adds a listener.
+ * @param l
+ */
+ public void addTreeSelectionListener(TreeSelectionListener l) {
+ if (!listeners.contains(l)) {
+ listeners.add(l);
+ }
+ }
+
+ /**
+ * Removes a listener.
+ * @param l
+ */
+ public void removeTreeSelectionListener(TreeSelectionListener l) {
+ listeners.remove(l);
+ }
+
+ /**
+ * Fires a tree selection event.
+ * @param newPath
+ */
+ public void fireTreeSelection(TreePath newPath) {
+ TreeSelectionEvent e = new TreeSelectionEvent(this,
+ newPath,
+ Utils.equalObjects(newPath, path),
+ path,
+ newPath);
+
+ for (TreeSelectionListener l : listeners) {
+ l.valueChanged(e);
+ }
+ }
+ //------------------------------------------------------------//
+
+ /**
+ * Invoked after a node (or a set of siblings) has changed in some way.
+ */
+ @Override
+ public void treeNodesChanged(TreeModelEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ regeneratePath();
+ repaint();
+ }
+ });
+ }
+
+ /**
+ * Invoked after nodes have been inserted into the tree.
+ */
+ @Override
+ public void treeNodesInserted(TreeModelEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ regeneratePath();
+ repaint();
+ }
+ });
+ }
+
+ /**
+ * Invoked after nodes have been removed from the tree.
+ */
+ @Override
+ public void treeNodesRemoved(TreeModelEvent e) {
+ selrect.unSet();
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ regeneratePath();
+ repaint();
+ }
+ });
+ }
+
+ /**
+ * Invoked after the tree has drastically changed structure from a
+ * given node down.
+ */
+ @Override
+ public void treeStructureChanged(TreeModelEvent e) {
+ selrect.unSet();
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ regeneratePath();
+ repaint();
+ }
+ });
+ }
+
+ //------------------------------------------------------------//
+
+ private Dimension preferredScrollableViewportSize =
+ new Dimension(320, 320);
+
+ public void setPreferredScrollableViewportSize(Dimension d) {
+ preferredScrollableViewportSize.setSize(d);
+ }
+
+ /**
+ * Returns the preferred size of the viewport for a view
+ * component.
+ */
+ @Override
+ public Dimension getPreferredScrollableViewportSize() {
+ return preferredScrollableViewportSize;
+ }
+
+ /**
+ * Components that display logical rows or columns should compute
+ * the scroll increment that will completely expose one block of
+ * rows or columns, depending on the value of orientation.
+ */
+ @Override
+ public int getScrollableBlockIncrement(Rectangle visibleRect,
+ int orientation,
+ int direction) {
+ return 16;
+ }
+
+ /**
+ * Return true if a viewport should always force the height of
+ * this Scrollable to match the height of the viewport.
+ */
+ @Override
+ public boolean getScrollableTracksViewportHeight() {
+ return false;
+ }
+
+ /**
+ * Return true if a viewport should always force the width of this
+ * Scrollable to match the width of the viewport.
+ */
+ @Override
+ public boolean getScrollableTracksViewportWidth() {
+ return false;
+ }
+
+ /**
+ * Components that display logical rows or columns should compute
+ * the scroll increment that will completely expose one new row or
+ * column, depending on the value of orientation.
+ */
+ @Override
+ public int getScrollableUnitIncrement(Rectangle visibleRect,
+ int orientation,
+ int direction) {
+ return 16;
+ }
+
+ /**
+ * Keylistener interface methods
+ */
+ @Override
+ public void keyTyped(KeyEvent e) {
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if(e.getKeyCode() == KeyEvent.VK_DELETE) {
+ System.out.println("DELETE: ");
+ if (path != null && path.getPathCount() > 1) {
+
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+ Element actual = node.actual();
+ Element link = node.link();
+
+ // actual object (or broken link)
+ if (link == null || node.isType(INCLUDE_OBJECT)) {
+ actual.getParentNode().removeChild(actual);
+ }
+ // working link (link != null)
+ else {
+ link.getParentNode().removeChild(link);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+}
diff --git a/src/objectview/ObjectViewPopup.java b/src/objectview/ObjectViewPopup.java
new file mode 100644
index 0000000..4420307
--- /dev/null
+++ b/src/objectview/ObjectViewPopup.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objectview;
+
+import javax.swing.tree.TreePath;
+import pooledit.TreeEditPopup;
+import treemodel.XMLTreeModel;
+
+
+/**
+ *
+ * @author jkalmari
+ */
+public class ObjectViewPopup extends TreeEditPopup {
+
+ private final ObjectView objectView;
+
+ /**
+ * Constructor.
+ * @param objectView
+ */
+ public ObjectViewPopup(ObjectView objectView){
+ this.objectView = objectView;
+ }
+
+ /**
+ * Gets the current path (which is in this case the active path from the
+ * object view).
+ * @return
+ */
+ @Override
+ public TreePath getCurrentPath() {
+ return objectView.getActivePath();
+ }
+
+ /**
+ * Gets the xml tree model.
+ * @return
+ */
+ @Override
+ public XMLTreeModel getXMLTreeModel() {
+ return objectView.getModel();
+ }
+}
diff --git a/src/objectview/PointObjectOperations.java b/src/objectview/PointObjectOperations.java
new file mode 100644
index 0000000..3f85edd
--- /dev/null
+++ b/src/objectview/PointObjectOperations.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objectview;
+
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.Vector;
+import javax.swing.tree.TreePath;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class PointObjectOperations implements ObjectOperations {
+
+ private final Point point = new Point();
+ private final Vector currentPath = new Vector();
+ private TreePath pointedPath;
+ private int depth = 0;
+
+ /**
+ * Constructor.
+ * @param x
+ * @param y
+ */
+ public PointObjectOperations(int x, int y) {
+ point.setLocation(x, y);
+ }
+
+ /**
+ * Gets the path to the pointed object.
+ * @return
+ */
+ public TreePath getPointedPath() {
+ return pointedPath;
+ }
+
+ /**
+ * Checks, whether the point is inside the specified node.
+ * @param gfx
+ * @param node
+ */
+ private void check(Graphics2D gfx, XMLTreeNode node) {
+ for (int i = currentPath.size() - 1; i >= depth; i--) {
+ currentPath.remove(i);
+ }
+ currentPath.add(node);
+
+ Shape clip = gfx.getClip();
+ if (gfx.getTransform().createTransformedShape(clip).contains(point)) {
+ pointedPath = new TreePath(currentPath.toArray());
+ }
+ //System.out.println("checking node: " + node + "(" + pointedNode + ")");
+ }
+
+ /**
+ * Sets the depth.
+ * @param depth
+ */
+ @Override
+ public void setDepth(int depth) {
+ this.depth = depth;
+ }
+
+ /**
+ * Icrements the depth by one.
+ */
+ @Override
+ public void incDepth() {
+ depth++;
+ }
+
+ /**
+ * Decrements the depth by one.
+ */
+ @Override
+ public void decDepth() {
+ depth--;
+ if (depth < 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Gets the depth.
+ * @return
+ */
+ @Override
+ public int getDepth() {
+ return depth;
+ }
+
+ @Override
+ public void opAlarmMask(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opArchedBarGraph(Graphics2D gfx, XMLTreeNode node) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opAuxiliaryFunction(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opAuxiliaryInput(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opButton(Graphics2D gfx, XMLTreeNode node) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opContainer(Graphics2D gfx, XMLTreeNode node) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opDataMask(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opEllipse(Graphics2D gfx, XMLTreeNode node, String imagepath) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opInputBoolean(Graphics2D gfx, XMLTreeNode node) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opInputList(Graphics2D gfx, XMLTreeNode node) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opInputNumber(Graphics2D gfx, XMLTreeNode node) throws IOException {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opInputString(Graphics2D gfx, XMLTreeNode node) throws IOException {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opKey(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opLine(Graphics2D gfx, XMLTreeNode node) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opLinearBarGraph(Graphics2D gfx, XMLTreeNode node) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opMeter(Graphics2D gfx, XMLTreeNode node) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opObjectPointer(Graphics2D gfx, XMLTreeNode node) {
+ // check(gfx, node); // no check
+ }
+
+ @Override
+ public void opOutputNumber(Graphics2D gfx, XMLTreeNode node) throws IOException {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opOutputString(Graphics2D gfx, XMLTreeNode node) throws IOException {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opPictureGraphic(Graphics2D gfx, XMLTreeNode node, BufferedImage image) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opPolygon(Graphics2D gfx, XMLTreeNode node, String imagepath) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opRectangle(Graphics2D gfx, XMLTreeNode node, String imagepath) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opSoftKeyMask(Graphics2D gfx, XMLTreeNode node, int w, int h, int sk_height) {
+ check(gfx, node);
+ }
+
+ @Override
+ public void opWorkingSet(Graphics2D gfx, XMLTreeNode node, int w, int h) {
+ check(gfx, node);
+ }
+}
diff --git a/src/objectview/SelectionRectangle.java b/src/objectview/SelectionRectangle.java
new file mode 100644
index 0000000..b9e1d02
--- /dev/null
+++ b/src/objectview/SelectionRectangle.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package objectview;
+
+import static pooledit.Definitions.*;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.util.ArrayList;
+import java.util.List;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.TexturePaint;
+import java.awt.image.BufferedImage;
+import treemodel.XMLTreeModel;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class SelectionRectangle {
+
+ static private final int IMAGE_SIZE = 4;
+ static private final int BORDER_WIDTH = 4;
+ static private final int CORNER_SIZE = 6;
+ static private final int POLY_SIZE = 8;
+
+ /** Corners and midpoints */
+ static private final int TOP_LEFT = 0;
+ static private final int TOP_CENTER = 1;
+ static private final int TOP_RIGHT = 2;
+ static private final int MIDDLE_LEFT = 3;
+ static private final int MIDDLE_CENTER = 4;
+ static private final int MIDDLE_RIGHT = 5;
+ static private final int BOTTOM_LEFT = 6;
+ static private final int BOTTOM_CENTER = 7;
+ static private final int BOTTOM_RIGHT = 8;
+ static private final int NRO_CORNERS = 9;
+
+ static private final Paint PAINT = createPaint();
+ static private Paint createPaint() {
+ BufferedImage image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE,
+ BufferedImage.TYPE_INT_ARGB);
+ for (int i = 0; i < IMAGE_SIZE; i++) {
+ image.setRGB(i, i, 0xFF000000);
+ }
+ Rectangle anchor = new Rectangle(0, 0, IMAGE_SIZE, IMAGE_SIZE);
+ return new TexturePaint(image, anchor);
+ }
+
+ static private Rectangle[] createCorners() {
+ Rectangle[] corners = new Rectangle[NRO_CORNERS];
+ for (int i = 0, n = corners.length; i < n; i++) {
+ corners[i] = new Rectangle();
+ }
+ return corners;
+ }
+
+ private final AffineTransform xform = new AffineTransform();
+ private final Rectangle[] corners;
+ private Rectangle rect;
+ private int corner = -1;
+
+ /** Temporary point used the transform polygon geometry */
+ private final Point pnt = new Point();
+ private final Point polystartpoint = new Point();
+ private final Polygon polygon = new Polygon();
+ private final List polynodes = new ArrayList();
+ private final List polycorners = new ArrayList();
+ private int polycorner = -1;
+
+ private XMLTreeNode source;
+
+ /**
+ * Constructor.
+ */
+ public SelectionRectangle() {
+ corners = createCorners();
+ }
+
+ /**
+ * Gets the rectangle.
+ * @return
+ */
+ public Rectangle getRectangle() {
+ return new Rectangle(rect);
+ }
+
+ /**
+ * Gets the source object (xml tree node).
+ * @return
+ */
+ public Object getSource() {
+ return source;
+ }
+
+ /**
+ * Checks, whether editing is in progress.
+ * @return
+ */
+ public boolean editInProgress() {
+ return corner >= 0 || polycorner >= 0;
+ }
+
+ /**
+ * Resets the selection rectangle, if editing is NOT in progress.
+ */
+ public void reset() {
+ if (!editInProgress()) {
+ rect = null;
+ polynodes.clear();
+ polycorners.clear();
+ }
+ }
+
+ /**
+ * Shape is in relative coordinates (to the current graphics object),
+ * so it has to be converted to absolute coordinates.
+ * @param xfrm
+ * @param shape
+ * @param source
+ */
+ public void set(AffineTransform xfrm, Shape shape, XMLTreeNode source) {
+ if (!editInProgress()) {
+ xform.setTransform(xfrm);
+ rect = xfrm.createTransformedShape(shape).getBounds();
+ this.source = source;
+ if (source.isType(POLYGON)) {
+ initPolyCorners(source);
+ }
+ }
+ }
+
+ /**
+ * Adjusts the selection rectangle by the given transformation.
+ * @param xfrm
+ */
+ public void adjust(AffineTransform xfrm) {
+ if (!editInProgress() && isSet()) {
+ xform.preConcatenate(xfrm);
+ rect = xfrm.createTransformedShape(rect).getBounds();
+ if (source.isType(POLYGON)) {
+ initPolyCorners(source);
+ }
+ }
+ }
+
+ /**
+ * Initializes the polygon corner circles.
+ * @param node
+ */
+ public void initPolyCorners(XMLTreeNode node) {
+ polynodes.clear();
+ polycorners.clear();
+ XMLTreeModel model = node.getModel();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ if (nd.isType(POINT)) {
+ pnt.setLocation(nd.getX(), nd.getY());
+ xform.transform(pnt, pnt);
+ polynodes.add(nd);
+ polycorners.add(new Ellipse2D.Double(pnt.x - (POLY_SIZE / 2),
+ pnt.y - (POLY_SIZE / 2), POLY_SIZE, POLY_SIZE));
+ }
+ }
+ }
+
+ public boolean isSet() {
+ return rect != null;
+ }
+
+ /**
+ * Call deselectCorners and then reset to get the same effect.
+ */
+ public void unSet() {
+ rect = null;
+ }
+
+ /**
+ * Selects polygon corner at the given coordinates. Returns false is no
+ * corner was selected.
+ * @param x
+ * @param y
+ * @return
+ */
+ public boolean selectPolyCornerAt(int x, int y) {
+ for (Ellipse2D e : polycorners) {
+ if (e.contains(x, y)) {
+ polystartpoint.setLocation(e.getX(), e.getY());
+ polycorner = polycorners.indexOf(e);
+ return true;
+ }
+ }
+ polycorner = -1;
+ return false;
+ }
+
+ /**
+ * Selects the corner at the given coordinates. Returns false if no corner
+ * was selected.
+ * @param x
+ * @param y
+ * @return
+ */
+ public boolean selectCornerAt(int x, int y) {
+ Rectangle[] c = getCorners();
+ for (int i = c.length - 1; i >= 0; i--) {
+ if (c[i].contains(x, y)) {
+ corner = i;
+ return true;
+ }
+ }
+ corner = -1;
+ return false;
+ }
+
+ /**
+ * Gets the selected polygon corner.
+ * @return
+ */
+ public XMLTreeNode getSelectedPolyCorner() {
+ return polynodes.get(polycorner);
+ }
+
+ /**
+ * Moves the selected polygon corner.
+ * @param dx
+ * @param dy
+ */
+ public void moveSelectedPolyCorner(int dx, int dy) {
+ Ellipse2D.Double e = polycorners.get(polycorner);
+ e.x += dx;
+ e.y += dy;
+ }
+
+ /**
+ * Moves selected corner.
+ * @param dx
+ * @param dy
+ */
+ public void moveSelectedCorner(int dx, int dy) {
+ switch (corner) {
+ case TOP_LEFT:
+ rect.x += dx;
+ rect.y += dy;
+ rect.width -= dx;
+ rect.height -= dy;
+ break;
+ case TOP_CENTER:
+ rect.y += dy;
+ rect.height -= dy;
+ break;
+ case TOP_RIGHT:
+ rect.y += dy;
+ rect.width += dx;
+ rect.height -= dy;
+ break;
+ case MIDDLE_LEFT:
+ rect.x += dx;
+ rect.width -= dx;
+ break;
+ case MIDDLE_CENTER:
+ rect.x += dx;
+ rect.y += dy;
+ break;
+ case MIDDLE_RIGHT:
+ rect.width += dx;
+ break;
+ case BOTTOM_LEFT:
+ rect.x += dx;
+ rect.width -= dx;
+ rect.height += dy;
+ break;
+ case BOTTOM_CENTER:
+ rect.height += dy;
+ break;
+ case BOTTOM_RIGHT:
+ rect.width += dx;
+ rect.height += dy;
+ break;
+ }
+ }
+
+ public void deselectCorners() {
+ corner = -1;
+ polycorner = -1;
+ }
+
+ /**
+ * Gets the corner rectangles.
+ * @return
+ */
+ public Rectangle[] getCorners() {
+ int xl = rect.x - CORNER_SIZE;
+ int xc = rect.x + (rect.width - CORNER_SIZE) / 2;
+ int xr = rect.x + rect.width;
+ int yt = rect.y - CORNER_SIZE;
+ int ym = rect.y + (rect.height - CORNER_SIZE) / 2;
+ int yb = rect.y + rect.height;
+
+ corners[TOP_LEFT].setRect(xl, yt, CORNER_SIZE, CORNER_SIZE);
+ corners[TOP_CENTER].setRect(xc, yt, CORNER_SIZE, CORNER_SIZE);
+ corners[TOP_RIGHT].setRect(xr, yt, CORNER_SIZE, CORNER_SIZE);
+ corners[MIDDLE_LEFT].setRect(xl, ym, CORNER_SIZE, CORNER_SIZE);
+ corners[MIDDLE_CENTER].setRect(xc, ym, CORNER_SIZE, CORNER_SIZE);
+ corners[MIDDLE_RIGHT].setRect(xr, ym, CORNER_SIZE, CORNER_SIZE);
+ corners[BOTTOM_LEFT].setRect(xl, yb, CORNER_SIZE, CORNER_SIZE);
+ corners[BOTTOM_CENTER].setRect(xc, yb, CORNER_SIZE, CORNER_SIZE);
+ corners[BOTTOM_RIGHT].setRect(xr, yb, CORNER_SIZE, CORNER_SIZE);
+ return corners;
+ }
+
+ /**
+ * Draws this selection rectangle.
+ * @param gfx
+ * @param zoom
+ */
+ public void draw(Graphics2D gfx, double zoom) {
+ int x = rect.x - BORDER_WIDTH;
+ int y = rect.y - BORDER_WIDTH;
+ int w = rect.width + 2 * BORDER_WIDTH;
+ int h = rect.height + 2 * BORDER_WIDTH;
+
+ gfx.setPaint(PAINT);
+ gfx.setXORMode(Color.WHITE);
+ gfx.fillRect(x, y, w, BORDER_WIDTH); // top
+ gfx.fillRect(x, rect.y + rect.height, w, BORDER_WIDTH); // bottom
+ gfx.fillRect(x, rect.y, BORDER_WIDTH, rect.height); // left
+ gfx.fillRect(rect.x + rect.width, rect.y, BORDER_WIDTH, rect.height); // left
+ if (source.isType(POLYGON)) {
+ drawPolygon(gfx, zoom);
+ }
+ gfx.setPaintMode();
+
+ drawCorners(gfx);
+ if (source.isType(POLYGON)) {
+ drawPolyCorners(gfx);
+ }
+ }
+
+ /**
+ * Draws the polygon corner circles.
+ * @param gfx
+ */
+ private void drawPolyCorners(Graphics2D gfx) {
+ for (Ellipse2D e : polycorners) {
+ gfx.setColor(polycorners.indexOf(e) == polycorner ? Color.RED : Color.WHITE);
+ gfx.fill(e);
+ gfx.setColor(Color.BLACK);
+ gfx.draw(e);
+ }
+ }
+
+ /**
+ * Draws the polygon.
+ * @param gfx
+ * @param zoom
+ */
+ public void drawPolygon(Graphics2D gfx, double zoom) {
+ polygon.reset();
+ for (int i = 0, n = polycorners.size(); i < n; i++) {
+ Ellipse2D.Double e = polycorners.get(i);
+ int x = (int) e.x;
+ int y = (int) e.y;
+ if (i == polycorner) {
+ x = polystartpoint.x + (int) ((int) ((e.x - polystartpoint.x) / zoom) * zoom);
+ y = polystartpoint.y + (int) ((int) ((e.y - polystartpoint.y) / zoom) * zoom);
+ }
+ polygon.addPoint(x + (POLY_SIZE / 2), y + (POLY_SIZE / 2));
+ }
+ gfx.draw(polygon);
+ }
+
+ /**
+ * Draws the corner rectangles.
+ * @param gfx
+ */
+ private void drawCorners(Graphics2D gfx) {
+ Rectangle[] c = getCorners();
+ for (int i = 0, n = c.length; i < n; i++) {
+ gfx.setColor(corner == i ? Color.RED : Color.WHITE);
+ gfx.fill(c[i]);
+ gfx.setColor(Color.BLACK);
+ gfx.draw(c[i]);
+ }
+ }
+}
diff --git a/src/pooledit/Definitions.java b/src/pooledit/Definitions.java
new file mode 100644
index 0000000..d164a15
--- /dev/null
+++ b/src/pooledit/Definitions.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package pooledit;
+
+/**
+ *
+ * @author mohman
+ */
+public class Definitions {
+
+ // root
+ static public final String OBJECTPOOL = "objectpool";
+
+ // categories
+ static public final String TOPLEVELOBJECTS_ = "TopLevelObjects";
+ static public final String KEYOBJECTS_ = "KeyObjects";
+ static public final String INPUTFIELDOBJECTS_ = "InputFieldObjects";
+ static public final String OUTPUTFIELDOBJECTS_ = "OutputFieldObjects";
+ static public final String SHAPEOBJECTS_ = "ShapeObjects";
+ static public final String GRAPHICSOBJECTS_ = "GraphicsObjects";
+ static public final String PICTUREOBJECTS_ = "PictureObjects";
+ static public final String VARIABLEOBJECTS_ = "VariableObjects";
+ static public final String ATTRIBUTESOBJECTS_ = "AttributeObjects";
+ static public final String POINTEROBJECTS_ = "PointerObjects";
+ static public final String MACROOBJECTS_ = "MacroObjects";
+ static public final String AUXILIARYOBJECTS_ = "AuxiliaryObjects";
+
+ static public final String[] CATEGORIES = {
+ TOPLEVELOBJECTS_, KEYOBJECTS_, INPUTFIELDOBJECTS_, OUTPUTFIELDOBJECTS_,
+ SHAPEOBJECTS_, GRAPHICSOBJECTS_, PICTUREOBJECTS_, VARIABLEOBJECTS_,
+ ATTRIBUTESOBJECTS_, POINTEROBJECTS_, MACROOBJECTS_, AUXILIARYOBJECTS_
+ };
+
+ // sub-categories
+ static public final String WORKINGSETS_ = "WorkingSets";
+ static public final String DATAMASKS_ = "DataMasks";
+ static public final String ALARMMASKS_ = "AlarmMasks";
+ static public final String CONTAINERS_ = "Containers";
+ static public final String SOFTKEYMASKS_ = "SoftkeyMasks";
+ static public final String KEYS_ = "Keys";
+ static public final String BUTTONS_ = "Buttons";
+ static public final String INPUTBOOLEANFIELDS_ = "InputBooleanFields";
+ static public final String INPUTSTRINGFIELDS_ = "InputStringFields";
+ static public final String INPUTNUMBERFIELDS_ = "InputNumberFields";
+ static public final String INPUTLISTFIELDS_ = "InputListFields";
+ static public final String OUTPUTSTRINGFIELDS_ = "OutputStringFields";
+ static public final String OUTPUTNUMBERFIELDS_ = "OutputNumberFields";
+ static public final String LINES_ = "Lines";
+ static public final String RECTANGLES_ = "Rectangles";
+ static public final String ELLIPSES_ = "Ellipses";
+ static public final String POLYGONS_ = "Polygons";
+ static public final String METERS_ = "Meters";
+ static public final String LINEARBARGRAPHS_ = "LinearBarGraphs";
+ static public final String ARCHEDBARGRAPHS_ = "ArchedBarGraphs";
+ static public final String PICTURES_ = "Pictures";
+ static public final String NUMBERVARIABLES_ = "NumberVariables";
+ static public final String STRINGVARIABLES_ = "StringVariables";
+ static public final String FONTATTRIBUTES_ = "FontAttributes";
+ static public final String LINEATTRIBUTES_ = "LineAttributes";
+ static public final String FILLATTRIBUTES_ = "FillAttributes";
+ static public final String INPUTATTRIBUTES_ = "InputAttributes";
+ static public final String OBJECTPOINTERS_ = "ObjectPointers";
+ static public final String MACROS_ = "Macros";
+ static public final String AUXILIARYFUNCTIONS_ = "AuxiliaryFunctions";
+ static public final String AUXILIARYINPUTS_ = "AuxiliaryInputs";
+
+ static public final String[][] SUBCATEGORYGROUPS = {
+ {WORKINGSETS_, DATAMASKS_, ALARMMASKS_, CONTAINERS_},
+ {SOFTKEYMASKS_, KEYS_, BUTTONS_},
+ {INPUTBOOLEANFIELDS_, INPUTSTRINGFIELDS_, INPUTNUMBERFIELDS_, INPUTLISTFIELDS_},
+ {OUTPUTSTRINGFIELDS_, OUTPUTNUMBERFIELDS_},
+ {LINES_, RECTANGLES_, ELLIPSES_, POLYGONS_},
+ {METERS_, LINEARBARGRAPHS_, ARCHEDBARGRAPHS_},
+ {PICTURES_},
+ {NUMBERVARIABLES_, STRINGVARIABLES_},
+ {FONTATTRIBUTES_, LINEATTRIBUTES_, FILLATTRIBUTES_, INPUTATTRIBUTES_},
+ {OBJECTPOINTERS_},
+ {MACROS_},
+ {AUXILIARYFUNCTIONS_, AUXILIARYINPUTS_}
+ };
+
+ static public final String[] SUBCATEGORIES = {
+ WORKINGSETS_, DATAMASKS_, ALARMMASKS_, CONTAINERS_,
+ SOFTKEYMASKS_, KEYS_, BUTTONS_,
+ INPUTBOOLEANFIELDS_, INPUTSTRINGFIELDS_, INPUTNUMBERFIELDS_, INPUTLISTFIELDS_,
+ OUTPUTSTRINGFIELDS_, OUTPUTNUMBERFIELDS_,
+ LINES_, RECTANGLES_, ELLIPSES_, POLYGONS_,
+ METERS_, LINEARBARGRAPHS_, ARCHEDBARGRAPHS_,
+ PICTURES_,
+ NUMBERVARIABLES_, STRINGVARIABLES_,
+ FONTATTRIBUTES_, LINEATTRIBUTES_, FILLATTRIBUTES_, INPUTATTRIBUTES_,
+ OBJECTPOINTERS_,
+ MACROS_,
+ AUXILIARYFUNCTIONS_, AUXILIARYINPUTS_
+ };
+
+ // objects
+ static public final String WORKINGSET = "workingset";
+ static public final String DATAMASK = "datamask";
+ static public final String ALARMMASK = "alarmmask";
+ static public final String CONTAINER = "container";
+ static public final String SOFTKEYMASK = "softkeymask";
+ static public final String KEY = "key";
+ static public final String BUTTON = "button";
+ static public final String INPUTBOOLEAN = "inputboolean";
+ static public final String INPUTSTRING = "inputstring";
+ static public final String INPUTNUMBER = "inputnumber";
+ static public final String INPUTLIST = "inputlist";
+ static public final String OUTPUTSTRING = "outputstring";
+ static public final String OUTPUTNUMBER = "outputnumber";
+ static public final String LINE = "line";
+ static public final String RECTANGLE = "rectangle";
+ static public final String ELLIPSE = "ellipse";
+ static public final String POLYGON = "polygon";
+ static public final String METER = "meter";
+ static public final String LINEARBARGRAPH = "linearbargraph";
+ static public final String ARCHEDBARGRAPH = "archedbargraph";
+ static public final String PICTUREGRAPHIC = "picturegraphic";
+ static public final String NUMBERVARIABLE = "numbervariable";
+ static public final String STRINGVARIABLE = "stringvariable";
+ static public final String FONTATTRIBUTES = "fontattributes";
+ static public final String LINEATTRIBUTES = "lineattributes";
+ static public final String FILLATTRIBUTES = "fillattributes";
+ static public final String INPUTATTRIBUTES = "inputattributes";
+ static public final String OBJECTPOINTER = "objectpointer";
+ static public final String MACRO = "macro";
+ static public final String AUXILIARYFUNCTION = "auxiliaryfunction";
+ static public final String AUXILIARYINPUT = "auxiliaryinput";
+
+ static public final String[] OBJECTS = {
+ WORKINGSET, DATAMASK, ALARMMASK, CONTAINER,
+ SOFTKEYMASK, KEY, BUTTON,
+ INPUTBOOLEAN, INPUTSTRING,
+ INPUTNUMBER, INPUTLIST,
+ OUTPUTSTRING, OUTPUTNUMBER,
+ LINE, RECTANGLE, ELLIPSE, POLYGON,
+ METER, LINEARBARGRAPH, ARCHEDBARGRAPH, PICTUREGRAPHIC,
+ NUMBERVARIABLE, STRINGVARIABLE,
+ FONTATTRIBUTES, LINEATTRIBUTES, FILLATTRIBUTES, INPUTATTRIBUTES,
+ OBJECTPOINTER, MACRO, AUXILIARYFUNCTION, AUXILIARYINPUT
+ };
+
+ // pseudo objects
+ static public final String POINT = "point";
+ static public final String LANGUAGE = "language";
+ static public final String COMMAND = "command";
+
+ static public final String FIXEDBITMAP = "fixedbitmap";
+
+ // links
+ static public final String INCLUDE_OBJECT = "include_object";
+
+ // tree elements
+ static public final String[] TREE_ELEMENTS = {
+ OBJECTPOOL,
+ TOPLEVELOBJECTS_, KEYOBJECTS_, INPUTFIELDOBJECTS_, OUTPUTFIELDOBJECTS_,
+ SHAPEOBJECTS_, GRAPHICSOBJECTS_, PICTUREOBJECTS_, VARIABLEOBJECTS_,
+ ATTRIBUTESOBJECTS_, POINTEROBJECTS_, MACROOBJECTS_, AUXILIARYOBJECTS_,
+ WORKINGSETS_, DATAMASKS_, ALARMMASKS_, CONTAINERS_,
+ SOFTKEYMASKS_, KEYS_, BUTTONS_,
+ INPUTBOOLEANFIELDS_, INPUTSTRINGFIELDS_, INPUTNUMBERFIELDS_, INPUTLISTFIELDS_,
+ OUTPUTSTRINGFIELDS_, OUTPUTNUMBERFIELDS_,
+ LINES_, RECTANGLES_, ELLIPSES_, POLYGONS_,
+ METERS_, LINEARBARGRAPHS_, ARCHEDBARGRAPHS_,
+ PICTURES_,
+ NUMBERVARIABLES_, STRINGVARIABLES_,
+ FONTATTRIBUTES_, LINEATTRIBUTES_, FILLATTRIBUTES_, INPUTATTRIBUTES_,
+ OBJECTPOINTERS_,
+ MACROS_,
+ AUXILIARYFUNCTIONS_, AUXILIARYINPUTS_,
+ WORKINGSET, DATAMASK, ALARMMASK, CONTAINER,
+ SOFTKEYMASK, KEY, BUTTON,
+ INPUTBOOLEAN, INPUTSTRING,
+ INPUTNUMBER, INPUTLIST,
+ OUTPUTSTRING, OUTPUTNUMBER,
+ LINE, RECTANGLE, ELLIPSE, POLYGON,
+ METER, LINEARBARGRAPH, ARCHEDBARGRAPH, PICTUREGRAPHIC,
+ NUMBERVARIABLE, STRINGVARIABLE,
+ FONTATTRIBUTES, LINEATTRIBUTES, FILLATTRIBUTES, INPUTATTRIBUTES,
+ OBJECTPOINTER, MACRO, AUXILIARYFUNCTION, AUXILIARYINPUT,
+ POINT, LANGUAGE, INCLUDE_OBJECT, FIXEDBITMAP, COMMAND
+ };
+
+ // static public final String TREE_ELEMENT_PATH = "images\\";
+
+ static public final String TREE_ELEMENT_PATH = "/images/";
+
+ static public final String[] TREE_ELEMENT_FILENAMES = {
+ // --- root ---
+ "objectpoolobjects.png",
+ // --- categories ---
+ "toplevelobjects.png", "keyobjects.png", "inputfieldobjects.png", "outputfieldobjects.png",
+ "outputshapeobjects.png", "outputgraphicobjects.png", "picturegraphicobjects.png", "variableobjects.png",
+ "attributeobjects.png", "pointerobjects.png", "macroobjects.png", "auxiliarycontrolobjects.png",
+ // --- subcategories ---
+ "workingset.png", "datamask.png", "alarmmask.png", "container.png",
+ "softkeymask.png", "softkey.png", "button.png",
+ "inputbooleanfield.png", "inputstringfield.png", "inputnumberfield.png", "inputlistfield.png",
+ "outputstringfield.png", "outputnumberfield.png",
+ "line.png", "rectangle.png", "ellipse.png", "polygon.png",
+ "meter.png", "linearbargraph.png", "archedbargraph.png",
+ "picturegraphic.png",
+ "numbervariable.png", "stringvariable.png",
+ "fontattribute.png", "lineattribute.png", "fillattribute.png", "inputattribute.png",
+ "objectpointer.png",
+ "macro.png",
+ // --- objects ---
+ "auxiliaryfunction.png", "auxiliaryinput.png",
+ "workingset.png", "datamask.png", "alarmmask.png", "container.png",
+ "softkeymask.png", "softkey.png", "button.png",
+ "inputbooleanfield.png", "inputstringfield.png", "inputnumberfield.png", "inputlistfield.png",
+ "outputstringfield.png", "outputnumberfield.png",
+ "line.png", "rectangle.png", "ellipse.png", "polygon.png",
+ "meter.png", "linearbargraph.png", "archedbargraph.png",
+ "picturegraphic.png",
+ "numbervariable.png", "stringvariable.png",
+ "fontattribute.png", "lineattribute.png", "fillattribute.png", "inputattribute.png",
+ "objectpointer.png",
+ "macro.png",
+ "auxiliaryfunction.png", "auxiliaryinput.png",
+ // --- pseudo objects ---
+ "point.png", "language.png", "includeobject.png", "fixedbitmap.png", "command.png"
+ };
+
+ // attributes
+ static public final String ACTIVE_MASK = "active_mask";
+ static public final String SOFT_KEY_MASK = "soft_key_mask";
+ static public final String FONT_ATTRIBUTES = "font_attributes";
+ static public final String FOREGROUND_COLOUR = "foreground_colour"; // this is the font attribute in input boolean
+ static public final String INPUT_ATTRIBUTES = "input_attributes";
+ static public final String VARIABLE_REFERENCE = "variable_reference";
+ static public final String TARGET_VALUE_VARIABLE_REFERENCE = "target_value_variable_reference";
+ static public final String LINE_ATTRIBUTES = "line_attributes";
+ static public final String FILL_ATTRIBUTES = "fill_attributes";
+ static public final String FILL_PATTERN = "fill_pattern";
+ static public final String VALUE = "value";
+ static public final String TARGET_VALUE = "target_value";
+
+ static public final String NAME = "name";
+ static public final String ID = "id";
+ static public final String POS_X = "pos_x";
+ static public final String POS_Y = "pos_y";
+ static public final String FONT_SIZE = "font_size";
+ static public final String NUMBER_OF_DECIMALS = "number_of_decimals";
+ static public final String WIDTH = "width";
+ static public final String HEIGHT = "height";
+ static public final String LENGTH = "length";
+ static public final String START_ANGLE = "start_angle";
+ static public final String END_ANGLE = "end_angle";
+ static public final String LINE_DIRECTION = "line_direction";
+ static public final String DIMENSION = "dimension";
+ static public final String SK_WIDTH = "sk_width";
+ static public final String SK_HEIGHT = "sk_height";
+ static public final String FIX_BITMAP_PATH = "fix_bitmap_path";
+ static public final String STD_BITMAP_PATH = "std_bitmap_path";
+ static public final String HORIZONTAL_JUSTIFICATION = "horizontal_justification";
+ static public final String LINE_SUPPRESSION = "line_suppression";
+ static public final String FILL_COLOUR = "fill_colour";
+ static public final String COLOUR = "colour";
+ static public final String FONT_COLOUR = "font_colour";
+ static public final String BACKGROUND_COLOUR = "background_colour";
+ static public final String LINE_COLOUR = "line_colour";
+ static public final String TRANSPARENCY_COLOUR = "transparency_colour";
+ static public final String LINE_WIDTH = "line_width";
+ static public final String LINE_ART = "line_art";
+ static public final String FILL_TYPE = "fill_type";
+ static public final String FILE = "file";
+ static public final String FILE1 = "file1";
+ static public final String FILE4 = "file4";
+ static public final String FILE8 = "file8";
+ static public final String ELLIPSE_TYPE = "ellipse_type";
+ static public final String HIDDEN = "hidden";
+ static public final String POLYGON_TYPE = "polygon_type";
+ static public final String OPTIONS = "options";
+ static public final String NEEDLE_COLOUR = "needle_colour";
+ static public final String BORDER_COLOUR = "border_colour";
+ static public final String TARGET_LINE_COLOUR = "target_line_colour";
+ static public final String ARC_AND_TICK_COLOUR = "arc_and_tick_colour";
+ static public final String MIN_VALUE = "min_value";
+ static public final String MAX_VALUE = "max_value";
+ static public final String NUMBER_OF_TICKS = "number_of_ticks";
+ static public final String BAR_GRAPH_WIDTH = "bar_graph_width";
+ static public final String SCALE = "scale";
+ static public final String OFFSET = "offset";
+ static public final String CODE = "code";
+ static public final String SELECTABLE = "selectable";
+ static public final String LATCHABLE = "latchable";
+ static public final String ENABLED = "enabled";
+ static public final String HIDE_SHOW = "hide_show";
+ static public final String ENABLE_DISABLE = "enable_disable";
+ static public final String FORMAT = "format";
+ static public final String RLE = "rle";
+ static public final String FONT_TYPE = "font_type";
+ static public final String FONT_STYLE = "font_style";
+ static public final String PRIORITY = "priority";
+ static public final String ACOUSTIC_SIGNAL = "acoustic_signal";
+ static public final String FUNCTION_TYPE = "function_type";
+ static public final String VALIDATION_TYPE = "validation_type";
+ static public final String MASK_TYPE = "mask_type";
+
+ // pseudo attributes
+ static public final String BLOCK_FONT = "block_font";
+ static public final String BLOCK_ROW = "block_row";
+ static public final String BLOCK_COL = "block_col";
+
+ // macro roles
+ static public final String ON_ACTIVATE = "on_activate";
+ static public final String ON_DEACTIVATE = "on_deactivate";
+ static public final String ON_SHOW = "on_show";
+ static public final String ON_HIDE = "on_hide";
+ // static public final String ON_REFRESH = "on_refresh"; // cannot be used in macros!
+ static public final String ON_ENABLE = "on_enable";
+ static public final String ON_DISABLE = "on_disable";
+ static public final String ON_CHANGE_ACTIVE_MASK = "on_change_active_mask";
+ static public final String ON_CHANGE_SOFT_KEY_MASK = "on_change_soft_key_mask";
+ static public final String ON_CHANGE_ATTRIBUTE = "on_change_attribute";
+ static public final String ON_CHANGE_BACKGROUND_COLOUR = "on_change_background_colour";
+ static public final String ON_CHANGE_FONT_ATTRIBUTES = "on_change_font_attributes";
+ static public final String ON_CHANGE_LINE_ATTRIBUTES = "on_change_line_attributes";
+ static public final String ON_CHANGE_FILL_ATTRIBUTES = "on_change_fill_attributes";
+ static public final String ON_CHANGE_CHILD_LOCATION = "on_change_child_location";
+ static public final String ON_CHANGE_SIZE = "on_change_size";
+ static public final String ON_CHANGE_VALUE = "on_change_value";
+ static public final String ON_CHANGE_PRIORITY = "on_change_priority";
+ static public final String ON_CHANGE_END_POINT = "on_change_end_point";
+ static public final String ON_INPUT_FIELD_SELECTION = "on_input_field_selection";
+ static public final String ON_INPUT_FIELD_DESELECTION = "on_input_field_deselection";
+ static public final String ON_ESC = "on_esc";
+ static public final String ON_ENTRY_OF_VALUE = "on_entry_of_value";
+ static public final String ON_ENTRY_OF_NEW_VALUE = "on_entry_of_new_value";
+ static public final String ON_KEY_PRESS = "on_key_press";
+ static public final String ON_KEY_RELEASE = "on_key_release";
+ static public final String ON_CHANGE_CHILD_POSITION = "on_change_child_position";
+
+ //static public final String ATTRIBUTE = "attribute";
+ static public final String ROLE = "role";
+ static public final String BLOCK_FONT_SIZE = "block_font_size";
+ static public final String IMAGE_WIDTH = "image_width";
+ static public final String IMAGE_HEIGHT = "image_height";
+ static public final String USE = "use";
+
+ // special elements
+ static public final String IMAGE_DATA = "image_data";
+
+
+ static public final String COMMAND_HIDE_SHOW_OBJECT = "command_hide_show_object";
+ static public final String COMMAND_ENABLE_DISABLE_OBJECT = "command_enable_disable_object";
+ static public final String COMMAND_SELECT_INPUT_OBJECT = "command_select_input_object";
+ static public final String COMMAND_CONTROL_AUDIO_DEVICE = "command_control_audio_device";
+ static public final String COMMAND_SET_AUDIO_VOLUME = "command_set_audio_volume";
+ static public final String COMMAND_CHANGE_CHILD_LOCATION = "command_change_child_location";
+ static public final String COMMAND_CHANGE_CHILD_POSITION = "command_change_child_position";
+ static public final String COMMAND_CHANGE_SIZE = "command_change_size";
+ static public final String COMMAND_CHANGE_BACKGROUND_COLOUR = "command_change_background_colour";
+ static public final String COMMAND_CHANGE_NUMERIC_VALUE = "command_change_numeric_value";
+ static public final String COMMAND_CHANGE_STRING_VALUE = "command_change_string_value";
+ static public final String COMMAND_CHANGE_END_POINT = "command_change_end_point";
+ static public final String COMMAND_CHANGE_FONT_ATTRIBUTES = "command_change_font_attributes";
+ static public final String COMMAND_CHANGE_LINE_ATTRIBUTES = "command_change_line_attributes";
+ static public final String COMMAND_CHANGE_FILL_ATTRIBUTES = "command_change_fill_attributes";
+ static public final String COMMAND_CHANGE_ACTIVE_MASK = "command_change_active_mask";
+ static public final String COMMAND_CHANGE_SOFT_KEY_MASK = "command_change_soft_key_mask";
+ static public final String COMMAND_CHANGE_ATTRIBUTE = "command_change_attribute";
+ static public final String COMMAND_CHANGE_PRIORITY = "command_change_priority";
+ static public final String COMMAND_CHANGE_LIST_ITEM = "command_change_list_item";
+
+ static public final String[] COMMANDS = {
+ COMMAND_HIDE_SHOW_OBJECT, COMMAND_ENABLE_DISABLE_OBJECT,
+ COMMAND_SELECT_INPUT_OBJECT, COMMAND_CONTROL_AUDIO_DEVICE,
+ COMMAND_SET_AUDIO_VOLUME, COMMAND_CHANGE_CHILD_LOCATION,
+ COMMAND_CHANGE_CHILD_POSITION, COMMAND_CHANGE_SIZE,
+ COMMAND_CHANGE_BACKGROUND_COLOUR, COMMAND_CHANGE_NUMERIC_VALUE,
+ COMMAND_CHANGE_STRING_VALUE, COMMAND_CHANGE_END_POINT,
+ COMMAND_CHANGE_FONT_ATTRIBUTES, COMMAND_CHANGE_LINE_ATTRIBUTES,
+ COMMAND_CHANGE_FILL_ATTRIBUTES, COMMAND_CHANGE_ACTIVE_MASK,
+ COMMAND_CHANGE_SOFT_KEY_MASK, COMMAND_CHANGE_ATTRIBUTE,
+ COMMAND_CHANGE_PRIORITY, COMMAND_CHANGE_LIST_ITEM
+ };
+}
diff --git a/src/pooledit/DragLabel.java b/src/pooledit/DragLabel.java
new file mode 100644
index 0000000..e5f239e
--- /dev/null
+++ b/src/pooledit/DragLabel.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package pooledit;
+
+import static pooledit.Definitions.*;
+import java.util.List;
+import java.util.Map;
+import javax.swing.JLabel;
+import multidom.SingleDOM;
+import objecttree.ObjectTreeCellRenderer;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ *
+ * @author mohman
+ */
+public class DragLabel extends JLabel {
+
+ private final String type;
+ private final SingleDOM libdoc;
+
+ /**
+ * Constructor.
+ * @param type
+ * @param libdoc
+ */
+ public DragLabel(String type, SingleDOM libdoc) {
+ super(ObjectTreeCellRenderer.getIcon(type));
+ this.type = type;
+ this.libdoc = libdoc;
+ }
+
+ /**
+ * Gets the XML description of the first element with a matching type.
+ * @return
+ */
+ public String getXML() {
+ Document doc = libdoc.actual();
+ List list = Tools.getChildElementList(doc.getDocumentElement());
+ for (Element e : list) {
+ if (e.getNodeName().equals(type)) {
+ Map namemap = Tools.createNameMap(doc);
+ return Tools.writeToStringNoDec(Tools.createMergedElementRecursive(e, namemap));
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/pooledit/DynamicView.java b/src/pooledit/DynamicView.java
new file mode 100644
index 0000000..5f5800c
--- /dev/null
+++ b/src/pooledit/DynamicView.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package pooledit;
+
+import java.awt.Component;
+import javax.swing.Icon;
+import net.infonode.docking.View;
+
+/**
+ *
+ * @author mohman
+ */
+public class DynamicView extends View {
+ private int id;
+
+ /**
+ * Constructor.
+ * @param title the view title
+ * @param icon the view icon
+ * @param component the view component
+ * @param id the view id
+ */
+ public DynamicView(String title, Icon icon, Component component, int id) {
+ super(title, icon, component);
+ this.id = id;
+ }
+
+ /**
+ * Returns the view id.
+ * @return the view id
+ */
+ public int getId() {
+ return id;
+ }
+}
diff --git a/src/pooledit/Icons.java b/src/pooledit/Icons.java
new file mode 100644
index 0000000..5a56f7c
--- /dev/null
+++ b/src/pooledit/Icons.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package pooledit;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import javax.swing.Icon;
+
+/**
+ *
+ * @author mohman
+ */
+public abstract class Icons implements Icon {
+
+ private static final int ICON_SIZE = 14;
+ private static final int SYMBOL_SIZE = 8;
+ private static final int OFFSET = (ICON_SIZE - SYMBOL_SIZE) / 2;
+ private static final int MIDDLE = SYMBOL_SIZE / 2;
+
+ @Override
+ public int getIconHeight() { return ICON_SIZE; }
+ @Override
+ public int getIconWidth() { return ICON_SIZE; }
+
+ static public final Icon ZOOM_MINUS_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.translate(x + OFFSET, y + OFFSET);
+ g.setColor(Color.BLACK);
+ g.drawOval(0, 0, SYMBOL_SIZE, SYMBOL_SIZE);
+ g.drawLine(2, MIDDLE, SYMBOL_SIZE - 2, MIDDLE);
+ g.translate(-(x + OFFSET), -(y + OFFSET));
+ }
+ };
+
+ static public final Icon ZOOM_PLUS_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.translate(x + OFFSET, y + OFFSET);
+ g.setColor(Color.BLACK);
+ g.drawOval(0, 0, SYMBOL_SIZE, SYMBOL_SIZE);
+ g.drawLine(2, MIDDLE, SYMBOL_SIZE - 2, MIDDLE);
+ g.drawLine(MIDDLE, 2, MIDDLE, SYMBOL_SIZE - 2);
+ g.translate(-(x + OFFSET), -(y + OFFSET));
+ }
+ };
+
+ static public final Icon DRAW_BORDERS_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.translate(x + OFFSET, y + OFFSET);
+ g.setColor(Color.BLACK);
+ g.drawRect(0, 0, SYMBOL_SIZE, SYMBOL_SIZE);
+ g.translate(-(x + OFFSET), -(y + OFFSET));
+ }
+ };
+
+ static public final Icon IMAGE_ZOOM_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.translate(x + OFFSET, y + OFFSET);
+ g.drawRect(0, 0, SYMBOL_SIZE, SYMBOL_SIZE);
+ g.drawLine(2, 2, SYMBOL_SIZE - 2, 2);
+ g.drawLine(2, SYMBOL_SIZE - 2, SYMBOL_SIZE - 2, 2);
+ g.drawLine(2, SYMBOL_SIZE - 2, SYMBOL_SIZE - 2, SYMBOL_SIZE - 2);
+ g.translate(-(x + OFFSET), -(y + OFFSET));
+ }
+ };
+
+ static public final Icon DRAW_GRID_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.translate(x + OFFSET, y + OFFSET);
+ g.drawRect(0, 0, MIDDLE, MIDDLE);
+ g.drawRect(MIDDLE, 0, MIDDLE, MIDDLE);
+ g.drawRect(0, MIDDLE, MIDDLE, MIDDLE);
+ g.drawRect(MIDDLE, MIDDLE, MIDDLE, MIDDLE);
+ g.translate(-(x + OFFSET), -(y + OFFSET));
+ }
+ };
+
+ static public final Icon XML_PARSE_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.translate(x + OFFSET, y + OFFSET);
+ g.fillPolygon(new int[] {SYMBOL_SIZE, MIDDLE, 0},
+ new int[] {SYMBOL_SIZE, 0, SYMBOL_SIZE}, 3);
+ g.translate(-(x + OFFSET), -(y + OFFSET));
+ }
+ };
+
+ static public final Icon XML_GENERATE_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.translate(x + OFFSET, y + OFFSET);
+ g.fillPolygon(new int[] {0, MIDDLE, SYMBOL_SIZE},
+ new int[] {0, SYMBOL_SIZE, 0}, 3);
+ g.translate(-(x + OFFSET), -(y + OFFSET));
+ }
+ };
+
+ static public final Icon CLEAR_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.translate(x + OFFSET, y + OFFSET);
+ g.drawRect(0, 0, SYMBOL_SIZE, SYMBOL_SIZE);
+ g.drawLine(2, 2, SYMBOL_SIZE - 2, 2);
+ g.drawLine(2, 2, 2, SYMBOL_SIZE - 2);
+ g.drawLine(2, SYMBOL_SIZE - 2, SYMBOL_SIZE - 2, SYMBOL_SIZE - 2);
+ g.translate(-(x + OFFSET), -(y + OFFSET));
+ }
+ };
+
+ /**
+ * Custom view icon.
+ */
+ static public final Icon VIEW_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ Color oldColor = g.getColor();
+ int off = (ICON_SIZE - SYMBOL_SIZE) / 2;
+ g.setColor(new Color(70, 70, 70));
+ g.fillRect(x + off, y + off, SYMBOL_SIZE, SYMBOL_SIZE);
+
+ g.setColor(new Color(100, 230, 100));
+ g.fillRect(x + off + 1, y + off + 1, SYMBOL_SIZE - 2, SYMBOL_SIZE - 2);
+
+ g.setColor(oldColor);
+ }
+ };
+
+ /**
+ * Custom view button icon.
+ */
+ static public final Icon BUTTON_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ Color oldColor = g.getColor();
+
+ int off = (ICON_SIZE - SYMBOL_SIZE) / 2;
+ g.setColor(Color.BLACK);
+ g.fillOval(x + off, y + off, SYMBOL_SIZE, SYMBOL_SIZE);
+
+ g.setColor(oldColor);
+ }
+ };
+
+ /**
+ * Custom view button icon.
+ */
+ static public final Icon BUTTON_ROLLOVER_ICON = new Icons() {
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ Color oldColor = g.getColor();
+
+ g.setColor(Color.GRAY);
+ g.drawRect(x, y, ICON_SIZE - 1, ICON_SIZE - 1);
+
+ int off = (ICON_SIZE - SYMBOL_SIZE) / 2;
+ g.setColor(Color.RED /*Color.BLACK*/);
+ g.fillOval(x + off, y + off, SYMBOL_SIZE, SYMBOL_SIZE);
+
+ g.setColor(oldColor);
+ }
+ };
+
+ static public final Icon POOLEDIT_LOGO = Utils.createImageIcon("/images/pooleditlogo.png");
+
+}
diff --git a/src/pooledit/Main.java b/src/pooledit/Main.java
new file mode 100644
index 0000000..72ef575
--- /dev/null
+++ b/src/pooledit/Main.java
@@ -0,0 +1,1394 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package pooledit;
+
+import static pooledit.Definitions.*;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import color.ColorPalette;
+import javax.swing.text.PlainDocument;
+import java.awt.Dimension;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseListener;
+import javax.swing.BorderFactory;
+import javax.swing.TransferHandler;
+import javax.swing.border.Border;
+import javax.swing.JCheckBoxMenuItem;
+import attributetable.AttributeTable;
+import attributetable.AttributeTableModel;
+import java.awt.BorderLayout;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import javax.swing.ButtonGroup;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JToolBar;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.JFileChooser;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.tree.TreePath;
+import multidom.MultiDOM;
+import multidom.SingleDOM;
+import net.infonode.docking.DockingWindow;
+import net.infonode.docking.DockingWindowAdapter;
+import net.infonode.docking.OperationAbortedException;
+import net.infonode.docking.RootWindow;
+import net.infonode.docking.SplitWindow;
+import net.infonode.docking.TabWindow;
+import net.infonode.docking.View;
+import net.infonode.docking.ViewSerializer;
+import net.infonode.docking.mouse.DockingWindowActionMouseButtonListener;
+import net.infonode.docking.properties.RootWindowProperties;
+import net.infonode.docking.theme.BlueHighlightDockingTheme;
+import net.infonode.docking.theme.ClassicDockingTheme;
+import net.infonode.docking.theme.DefaultDockingTheme;
+import net.infonode.docking.theme.DockingWindowsTheme;
+import net.infonode.docking.theme.GradientDockingTheme;
+import net.infonode.docking.theme.ShapedGradientDockingTheme;
+import net.infonode.docking.theme.SlimFlatDockingTheme;
+import net.infonode.docking.theme.SoftBlueIceDockingTheme;
+import net.infonode.docking.util.DockingUtil;
+import net.infonode.docking.util.MixedViewHandler;
+import net.infonode.docking.util.ViewMap;
+import net.infonode.gui.laf.InfoNodeLookAndFeel;
+import net.infonode.util.Direction;
+import objecttree.ObjectTree;
+import objectview.ObjectView;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import treemodel.XMLTreeModel;
+import treemodel.XMLTreeNode;
+import wizard.LineTrendGenerator;
+import wizard.LineTrendWizard;
+import wizard.MeterGenerator;
+import wizard.MeterWizard;
+import wizard.TableGenerator;
+import wizard.TableWizard;
+import wizard.TrendGenerator;
+import wizard.TrendWizard;
+
+/**
+ *
+ * @author mohman
+ */
+public class Main {
+
+ private final MultiDOM multidom = new MultiDOM();
+
+ private final AttributeTableModel tablemodel = new AttributeTableModel();
+ private final DefaultStyledDocument plaindoc = new DefaultStyledDocument();
+ private final JTextArea docArea = new JTextArea(plaindoc);
+ private final JScrollPane docScroll = new JScrollPane(docArea);
+
+ private final PlainDocument msgDoc = new PlainDocument();
+ private final JTextArea msgArea = new JTextArea(msgDoc);
+ private final JScrollPane msgScroll = new JScrollPane(msgArea);
+ private final PrintStream msgOutput = new PrintStream(new OutputStream() {
+ @Override
+ public void write(byte[] b) {
+ msgArea.append(new String(b));
+ }
+ @Override
+ public void write(byte[] b, int off, int len) {
+ msgArea.append(new String(b, off, len));
+ }
+ @Override
+ public void write(int b) {
+ msgArea.append(new String(new byte[] {(byte) b}));
+ }
+ });
+
+ private final ObjectView objectview = new ObjectView();
+ private final AttributeTable attributetable = new AttributeTable(tablemodel);
+
+ private final View ovView = new View("Object View", Icons.VIEW_ICON, new JScrollPane(objectview));
+ private final View atView = new View("Attribute Table", Icons.VIEW_ICON, new JScrollPane(attributetable));
+ private final View xmlView = new View("XML Code", Icons.VIEW_ICON, docScroll);
+ private final View msgView = new View("Messages", Icons.VIEW_ICON, msgScroll);
+ private SingleDOM libdoc;
+
+ /** An array of the static views */
+ private final View[] views = new View[] {ovView, atView, xmlView, msgView};
+
+ /** Contains all the static views */
+ private final ViewMap viewMap = new ViewMap(views);
+
+ // The mixed view map makes it easy to mix static and dynamic
+ // views inside the same root window
+ private final MixedViewHandler handler = new MixedViewHandler(viewMap, new ViewSerializer() {
+ @Override
+ public void writeView(View view, ObjectOutputStream out) throws IOException {
+ out.writeInt(((DynamicView) view).getId());
+ }
+
+ @Override
+ public View readView(ObjectInputStream in) throws IOException {
+ return getDynamicView(in.readInt());
+ }
+ });
+
+ /** The one and only root window */
+ private RootWindow rootWindow = DockingUtil.createRootWindow(viewMap, handler, true);
+
+ /** The view menu items */
+ private JMenuItem[] viewItems = new JMenuItem[views.length];
+
+ /** Contains the dynamic views that has been added to the root window */
+ private HashMap dynamicViews = new HashMap();
+
+ /** The currently applied docking windows theme */
+ private DockingWindowsTheme currentTheme = new ShapedGradientDockingTheme();
+
+ /**
+ * In this properties object the modified property values for close
+ * buttons etc. are stored. This object is cleared when the theme is
+ * changed.
+ */
+ private RootWindowProperties properties = new RootWindowProperties();
+
+ /** Where the layouts are stored */
+ private byte[][] layouts = new byte[3][];
+
+ /** The application frame */
+ private JFrame frame = new JFrame("PoolEdit");
+
+ /** Filenames */
+ private static String TESTPOOL = "test.xml";
+ private static String LIBRARY = "library.xml";
+ private static String TEMPLATE = "template.xml";
+
+ /**
+ * Constructor
+ */
+ public Main() {
+ createRootWindow();
+ setDefaultLayout();
+ showFrame();
+ objectview.setFlashing(true);
+
+ // redirecting outputid
+ System.setOut(msgOutput);
+ System.setErr(msgOutput);
+ }
+
+ /**
+ * Creates a view component containing the specified text.
+ * @param text the text
+ * @return the view component
+ */
+ private static JComponent createViewComponent(String text) {
+ return new JScrollPane(new JTextArea());
+ }
+
+ /**
+ * Returns a dynamic view with specified id, reusing an existing
+ * view if possible.
+ * @param id the dynamic view id
+ * @return the dynamic view
+ */
+ private View getDynamicView(int id) {
+ View view = dynamicViews.get(new Integer(id));
+ if (view == null) {
+ view = new DynamicView("Dynamic View " + id, Icons.VIEW_ICON, createViewComponent(""), id);
+ }
+ return view;
+ }
+
+ /**
+ * Returns the next available dynamic view id.
+ * @return the next available dynamic view id
+ */
+ private int getDynamicViewId() {
+ int id = 0;
+ while (dynamicViews.containsKey(id)) {
+ id++;
+ }
+ return id;
+ }
+
+ /**
+ * Creates a new object tree view.
+ * @param sdom
+ * @return
+ */
+ private DynamicView createNewObjectTreeView(final SingleDOM sdom) {
+ final XMLTreeModel model = sdom.getTreeModel();
+ final ObjectTree objecttree = new ObjectTree(model);
+ final DynamicView view = new DynamicView(sdom.getJFileChooser().getSelectedFile().getName(),
+ Icons.VIEW_ICON, new JScrollPane(objecttree), getDynamicViewId());
+
+ // a listener is register to the newly created object tree that will
+ // notify the multidom whenever a new path is selected
+ objecttree.addTreeSelectionListener(new TreeSelectionListener() {
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
+ multidom.setActiveDocument(model.getDocument());
+ multidom.setActivePath(e.getPath());
+ }
+ });
+
+ // a listener is registered to the newly created single dom that will
+ // notify the object tree whenever the path is changed (in that sdom)
+ sdom.addPathChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ objecttree.setActivePath(multidom.getActivePath());
+ }
+ });
+
+ sdom.addNameChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ view.getViewProperties().setTitle(sdom.getJFileChooser().getSelectedFile().getName());
+ }
+ });
+
+ objecttree.addFocusListener(new FocusListener() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ multidom.setActiveDocument(model.getDocument());
+ multidom.firePathChange();
+ }
+ @Override
+ public void focusLost(FocusEvent e) {
+ }
+ });
+
+ return view;
+ }
+
+ /**
+ * Creates the root window and the views.
+ */
+ private void createRootWindow() {
+ // this is called when active document is changed
+ multidom.addDocumentChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ SingleDOM doc = multidom.getActiveDocument();
+ objectview.setModel(doc.getTreeModel());
+ tablemodel.setDocument(doc.actual());
+ int len = plaindoc.getLength();
+ try {
+ String text = Tools.writeToString(doc.actual());
+ plaindoc.replace(0, len, text, null);
+ /*
+ SimpleAttributeSet attrs = new SimpleAttributeSet();
+ StyleConstants.setForeground(attrs, Color.RED);
+ StyleConstants.setFontFamily(attrs, "Serif");
+ plaindoc.setCharacterAttributes(0,40, attrs, false); */
+
+ } catch (BadLocationException ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+
+ // this is called when active path is changed
+ multidom.addPathChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ TreePath path = multidom.getActivePath();
+ objectview.setActivePath(path);
+ tablemodel.setActivePath(path);
+ if (path == null) {
+ return;
+ }
+ try {
+ String text = plaindoc.getText(0, plaindoc.getLength());
+ Element element = ((XMLTreeNode)path.getLastPathComponent()).actual();
+
+ // find the place of the element in the text
+ if (element != null) {
+ int selectionStart = Tools.findElementFromString(text, element.getNodeName(), element.getAttribute(NAME));
+ if (selectionStart != -1) {
+ int selectionEnd = Tools.findElementEnd(text, element.getNodeName(), selectionStart);
+ //Calculate the location of the viewport and set the caret position
+ int line = docArea.getLineOfOffset(selectionStart);
+ int lines = docArea.getLineCount();
+ int totalHeight = docArea.getHeight();
+ int y = (line * totalHeight) / lines;
+ docScroll.getViewport().setViewPosition(new Point(0, y));
+ //docArea.setCaretPosition(selectionStart);
+ //docArea.moveCaretPosition(selectionEnd);
+ docArea.select(selectionStart, selectionEnd);
+ }
+ }
+ } catch (BadLocationException ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+
+ // this listener is called when user selects object from the object
+ // view
+ objectview.addTreeSelectionListener(new TreeSelectionListener() {
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
+ multidom.setActivePath(e.getPath());
+ }
+ });
+
+ // this listener is called when the user selects reference links in
+ // the attribute table
+ tablemodel.addTreeSelectionListener(new TreeSelectionListener() {
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
+ multidom.setActivePath(e.getPath());
+ }
+ });
+
+ docArea.setDragEnabled(true);
+
+ ovView.getCustomTabComponents().add(createButton(Icons.ZOOM_PLUS_ICON, "Zoom in", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ double z = objectview.getZoom() + 0.5;
+ if (z > 10) { z = 10; }
+ objectview.setZoom(z);
+ }
+ }));
+
+ ovView.getCustomTabComponents().add(createButton(Icons.ZOOM_MINUS_ICON, "Zoom out", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ double z = objectview.getZoom() - 0.5;
+ if (z < 0.5) { z = 0.5; }
+ objectview.setZoom(z);
+ }
+ }));
+
+ ovView.getCustomTabComponents().add(createButton(Icons.DRAW_BORDERS_ICON, "Show borders", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ objectview.setDrawBorders(!objectview.getDrawBorders());
+ }
+ }));
+
+ ovView.getCustomTabComponents().add(createButton(Icons.DRAW_GRID_ICON, "Show grid", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ objectview.setDrawGrid(!objectview.getDrawGrid());
+ }
+ }));
+
+ ovView.getCustomTabComponents().add(createButton(Icons.IMAGE_ZOOM_ICON, "Image zoom", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ objectview.setImageZoom(!objectview.getImageZoom());
+ }
+ }));
+
+ xmlView.getCustomTabComponents().add(createButton(Icons.XML_PARSE_ICON, "Parse XML", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ String text = plaindoc.getText(0, plaindoc.getLength());
+ multidom.parseActiveDocument(text);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }));
+
+ xmlView.getCustomTabComponents().add(createButton(Icons.XML_GENERATE_ICON, "Generate XML", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ SingleDOM sdom = multidom.getActiveDocument();
+ if (sdom == null) {
+ return;
+ }
+ Document doc = sdom.actual();
+ int len = plaindoc.getLength();
+ try {
+ plaindoc.replace(0, len, Tools.writeToString(doc), null);
+ } catch (BadLocationException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }));
+
+ msgView.getCustomTabComponents().add(createButton(Icons.CLEAR_ICON, "Clear messages", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ msgDoc.remove(0, msgDoc.getLength());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }));
+
+ // Set gradient theme. The theme properties object is the super
+ // object of our properties object, which means our property value
+ // settings will override the theme values
+ properties.addSuperObject(currentTheme.getRootWindowProperties());
+
+ // Our properties object is the super object of the root window
+ // properties object, so all property values of the theme and in
+ // our property object will be used by the root window
+ rootWindow.getRootWindowProperties().addSuperObject(properties);
+
+ // Enable the bottom window bar
+ rootWindow.getWindowBar(Direction.DOWN).setEnabled(true);
+
+ // Add a listener which shows dialogs when a window is closing or
+ // closed.
+ rootWindow.addListener(new DockingWindowAdapter() {
+ @Override
+ public void windowAdded(DockingWindow addedToWindow, DockingWindow addedWindow) {
+ updateViews(addedWindow, true);
+ }
+
+ @Override
+ public void windowRemoved(DockingWindow removedFromWindow, DockingWindow removedWindow) {
+ updateViews(removedWindow, false);
+ }
+
+ @Override
+ public void windowClosing(DockingWindow window) throws OperationAbortedException {
+ if (JOptionPane.showConfirmDialog(frame, "Really close window '" + window + "'?") != JOptionPane.YES_OPTION) {
+ throw new OperationAbortedException("Window close was aborted!");
+ }
+ }
+ });
+
+ // Add a mouse button listener that closes a window when it's
+ // clicked with the middle mouse button.
+ rootWindow.addTabMouseButtonListener(DockingWindowActionMouseButtonListener.MIDDLE_BUTTON_CLOSE_LISTENER);
+ }
+
+ /**
+ * Creates a button with the specified icon and action listener.
+ * @param icon
+ * @param description
+ * @param listener
+ * @return
+ */
+ private static JButton createButton(Icon icon, String description, ActionListener listener) {
+ JButton button = new JButton(icon);
+ button.setOpaque(false);
+ button.setBorder(null);
+ button.setFocusable(false);
+ button.setToolTipText(description);
+ button.addActionListener(listener);
+ return button;
+ }
+
+ /**
+ * Update view menu items and dynamic view map.
+ * @param window the window in which to search for views
+ * @param added if true the window was added
+ */
+ private void updateViews(DockingWindow window, boolean added) {
+ if (window instanceof View) {
+ if (window instanceof DynamicView) {
+ if (added) {
+ dynamicViews.put(new Integer(((DynamicView) window).getId()), (View) window);
+ } else {
+ dynamicViews.remove(new Integer(((DynamicView) window).getId()));
+ }
+ } else {
+ for (int i = 0; i < views.length; i++) {
+ if (views[i] == window && viewItems[i] != null) {
+ viewItems[i].setEnabled(!added);
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < window.getChildWindowCount(); i++) {
+ updateViews(window.getChildWindow(i), added);
+ }
+ }
+ }
+
+ /**
+ * Sets the default window layout.
+ */
+ private void setDefaultLayout() {
+
+ //TabWindow tabWindow = new TabWindow(views);
+ try {
+ View pool = createNewObjectTreeView(multidom.loadDocument(TESTPOOL)); // FIXME: should not be hard coded!
+ libdoc = multidom.loadDocument(LIBRARY);
+ View lib = createNewObjectTreeView(libdoc); // FIXME: should not be hard coded!
+ rootWindow.setWindow(
+ new SplitWindow(true, 0.6f,
+ new SplitWindow(false, 0.7f, new TabWindow(ovView), new TabWindow(new View[] {xmlView, msgView})),
+ new SplitWindow(false, 0.5f, new SplitWindow(true, 0.5f, new TabWindow(pool), new TabWindow(lib)), new TabWindow(atView))));
+ } catch (Exception e) {
+ e.printStackTrace();
+ rootWindow.setWindow(
+ new SplitWindow(true, 0.6f,
+ new SplitWindow(false, 0.7f, new TabWindow(ovView), new TabWindow(new View[] {xmlView, msgView})),
+ new TabWindow(atView)));
+ }
+ /*
+ WindowBar windowBar = rootWindow.getWindowBar(Direction.DOWN);
+
+ while (windowBar.getChildWindowCount() > 0)
+ windowBar.getChildWindow(0).close();
+
+ windowBar.addTab(views[3]);
+ */
+ }
+
+ /**
+ * Initializes the frame and shows it.
+ */
+ private void showFrame() {
+ frame.getContentPane().add(createToolBar(), BorderLayout.NORTH);
+ frame.getContentPane().add(rootWindow, BorderLayout.CENTER);
+ frame.setJMenuBar(createMenuBar());
+ frame.setSize(900, 700);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setVisible(true);
+ }
+
+ /**
+ * Creates the frame tool bar.
+ * @return the frame tool bar
+ */
+ private JToolBar createToolBar() {
+ JToolBar toolBar = new JToolBar();
+
+ TransferHandler hnd = new TransferHandler("XML");
+ MouseListener listener = new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent evt) {
+ JComponent comp = (JComponent)evt.getSource();
+ TransferHandler th = comp.getTransferHandler();
+ th.exportAsDrag(comp, evt, TransferHandler.COPY);
+ }
+ };
+ for (int i = 0, n = Definitions.OBJECTS.length; i < n; i++) {
+ JLabel lbl = new DragLabel(Definitions.OBJECTS[i], libdoc);
+ lbl.setTransferHandler(hnd);
+ lbl.addMouseListener(listener);
+ Border bdr = BorderFactory.createRaisedBevelBorder();
+ lbl.setBorder(bdr);
+ toolBar.add(lbl);
+ toolBar.addSeparator(new Dimension(2, 1));
+ }
+
+ /*
+ // This will show a button with a document icon, which will create an
+ // empty text window when dragged. The text window is useful for debugging
+ // as it supports drag and drop - xml objects can be dragged to it for visual
+ // inspection.
+ JLabel lbl = new JLabel(ObjectTreeCellRenderer.getIcon(Definitions.OBJECTPOOL));
+ Border bdr = BorderFactory.createRaisedBevelBorder();
+ lbl.setBorder(bdr);
+ toolBar.add(lbl);
+ new DockingWindowDragSource(lbl, new DockingWindowDraggerProvider() {
+ public DockingWindowDragger getDragger(MouseEvent mouseEvent) {
+ return getDynamicView(getDynamicViewId()).startDrag(rootWindow);
+ }
+ });
+ */
+ return toolBar;
+ }
+
+ /**
+ * Creates the frame menu bar.
+ * @return the menu bar
+ */
+ private JMenuBar createMenuBar() {
+ JMenuBar menu = new JMenuBar();
+ menu.add(createFileMenu());
+ menu.add(createWizardMenu());
+ //menu.add(createLayoutMenu()); // can lose open documents?
+ //menu.add(createFocusViewMenu()); // not needed?
+ menu.add(createThemesMenu());
+ menu.add(createPropertiesMenu());
+ menu.add(createWindowBarsMenu());
+ menu.add(createViewMenu());
+ menu.add(createColorMenu());
+ menu.add(createHelpMenu());
+ return menu;
+ }
+
+ /**
+ * Creates the color menu.
+ * @return
+ */
+ private JMenu createColorMenu() {
+ JMenu menu = new JMenu("Colors");
+
+ ButtonGroup group = new ButtonGroup();
+ JRadioButtonMenuItem rbMenuItem = new JRadioButtonMenuItem("1-Bit Colors");
+ group.add(rbMenuItem);
+ menu.add(rbMenuItem).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ objectview.setColorDepth(ColorPalette.COLOR_1BIT);
+ objectview.repaint();
+ }
+ });
+
+ rbMenuItem = new JRadioButtonMenuItem("4-Bit Colors");
+ group.add(rbMenuItem);
+ menu.add(rbMenuItem).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ objectview.setColorDepth(ColorPalette.COLOR_4BIT);
+ objectview.repaint();
+ }
+ });
+
+ rbMenuItem = new JRadioButtonMenuItem("8-Bit Colors");
+ rbMenuItem.setSelected(true);
+ group.add(rbMenuItem);
+ menu.add(rbMenuItem).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ objectview.setColorDepth(ColorPalette.COLOR_8BIT);
+ objectview.repaint();
+ }
+ });
+
+ JCheckBoxMenuItem reducePicColors = new JCheckBoxMenuItem("Reduce Picture Colors");
+ menu.add(reducePicColors).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ objectview.setReduceImages(!objectview.getReduceImages());
+ objectview.repaint();
+ }
+ });
+
+ JCheckBoxMenuItem disableFlashing = new JCheckBoxMenuItem("Disable Flashing");
+ menu.add(disableFlashing).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ objectview.setFlashing(!objectview.getFlashing());
+ objectview.repaint();
+ }
+ });
+
+ return menu;
+ }
+
+ /**
+ * Creates the wizard menu.
+ * @return
+ */
+ private JMenu createWizardMenu() {
+ JMenu menu = new JMenu("Wizard");
+
+ menu.add("Meter Wizard").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode node = getSafeStartNodeForWizard();
+ if (node != null) {
+ JFrame wizardFrame = new JFrame("Meter Wizard");
+ MeterWizard wizard = new MeterWizard();
+ String name = Tools.findFreeName("meter_wiz",
+ Tools.createNameMap(node.getModel().getDocument(), true));
+ // node.getModel().getNameMap());
+ MeterGenerator generator = new MeterGenerator(wizard, node, name);
+ wizardFrame.getContentPane().add(wizard);
+ wizardFrame.pack();
+ wizardFrame.setVisible(true);
+ }
+ }
+ });
+
+ menu.add("Table Wizard").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode node = getSafeStartNodeForWizard();
+ if (node != null) {
+ JFrame wizardFrame = new JFrame("Table Wizard");
+ TableWizard wizard = new TableWizard();
+ String name = Tools.findFreeName("table_wiz",
+ Tools.createNameMap(node.getModel().getDocument(), true));
+ // node.getModel().getNameMap());
+ TableGenerator generator = new TableGenerator(wizard, node, name);
+ wizardFrame.getContentPane().add(wizard);
+ wizardFrame.pack();
+ wizardFrame.setVisible(true);
+ }
+ }
+ });
+
+ menu.add("Trend Wizard").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode node = getSafeStartNodeForWizard();
+ if (node != null) {
+ JFrame wizardFrame = new JFrame("Trend Wizard");
+ TrendWizard wizard = new TrendWizard();
+ String name = Tools.findFreeName("trend_wiz",
+ Tools.createNameMap(node.getModel().getDocument(), true));
+ // node.getModel().getNameMap());
+ TrendGenerator generator = new TrendGenerator(wizard, node, name);
+ wizardFrame.getContentPane().add(wizard);
+ wizardFrame.pack();
+ wizardFrame.setVisible(true);
+ }
+ }
+ });
+
+ menu.add("Line Trend Wizard").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode node = getSafeStartNodeForWizard();
+ if (node != null) {
+ JFrame wizardFrame = new JFrame("Line Trend Wizard");
+ LineTrendWizard wizard = new LineTrendWizard();
+ String name = Tools.findFreeName("line_trend_wiz",
+ Tools.createNameMap(node.getModel().getDocument(), true));
+ // node.getModel().getNameMap());
+ LineTrendGenerator generator = new LineTrendGenerator(wizard, node, name);
+ wizardFrame.getContentPane().add(wizard);
+ wizardFrame.pack();
+ wizardFrame.setVisible(true);
+ }
+ }
+ });
+ return menu;
+ }
+
+ /**
+ * A convenience method for getting a safe starting node for wizards.
+ * @return
+ */
+ private XMLTreeNode getSafeStartNodeForWizard() {
+ TreePath path = multidom.getActivePath();
+ if (path == null) {
+ JOptionPane.showMessageDialog(frame,
+ "No path is currently selected!",
+ "Wizard Error", JOptionPane.ERROR_MESSAGE);
+ return null;
+ }
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+ if (!node.isType(OBJECTS)) {
+ node = (XMLTreeNode) node.getModel().getRoot(); // -> OBJECTPOOL
+ }
+
+ if (!node.isType(OBJECTPOOL, DATAMASK, ALARMMASK, CONTAINER)) {
+ JOptionPane.showMessageDialog(frame,
+ "Path should be pointing to a objectpool, datamask, alarmmask or container object!",
+ "Wizard Error", JOptionPane.ERROR_MESSAGE);
+ return null;
+ }
+ return node;
+ }
+
+ /**
+ * Creates the file menu.
+ * @return
+ */
+ private JMenu createFileMenu() {
+ JMenu layoutMenu = new JMenu("File");
+
+ layoutMenu.add("New").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ JFileChooser fc = new JFileChooser();
+ fc.setSelectedFile(new File(TEMPLATE));
+ SingleDOM singleDom = multidom.loadDocument(fc.getSelectedFile().toURI().getPath());
+ singleDom.setJFileChooser(fc);
+ View view = createNewObjectTreeView(singleDom);
+ multidom.setActiveDocument(singleDom); //is this necessary?
+ TabWindow twin = new TabWindow(view);
+ rootWindow.setWindow(new SplitWindow(true, 0.8f, rootWindow.getWindow(), twin));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+
+ layoutMenu.add("Open...").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ // Creating a new JFileChooser (not using the current active)
+ JFileChooser fc = new JFileChooser();
+ fc.setFileFilter(new FileNameExtensionFilter("XML-file", "xml") );
+
+ int rv = fc.showOpenDialog(frame);
+ if (rv != JFileChooser.APPROVE_OPTION) {
+ return;
+ }
+ File file = fc.getSelectedFile();
+ if (!file.exists()) {
+ JOptionPane.showMessageDialog(frame,
+ "Document cound not be loaded.",
+ "File Not Found", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ try {
+ SingleDOM singleDom = multidom.loadDocument(file.toURI().getPath());
+ singleDom.setJFileChooser(fc);
+
+ // create view and open it
+ View view = createNewObjectTreeView( singleDom );
+ final TabWindow twin = new TabWindow(view);
+ rootWindow.setWindow(new SplitWindow(true, 0.8f, rootWindow.getWindow(), twin));
+
+ // set active
+ multidom.setActiveDocument(singleDom);
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+
+ layoutMenu.addSeparator();
+
+ layoutMenu.add("Validate").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ SingleDOM doc = multidom.getActiveDocument();
+ if (doc == null) {
+ JOptionPane.showMessageDialog(frame,
+ "No active document selected!",
+ "Validate Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ try {
+ if (Tools.validateDocument(msgOutput, doc.actual())) {
+ System.out.println("No errors detected");
+ }
+ else {
+ System.out.println("Pool has errors");
+ }
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+
+ layoutMenu.addSeparator();
+
+ layoutMenu.add("Save").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ SingleDOM doc = multidom.getActiveDocument();
+ if (doc == null) {
+ JOptionPane.showMessageDialog(frame,
+ "No active document selected!",
+ "Save Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ try {
+ multidom.saveDocument(doc);
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+
+ // FIXME: if user adds no extension, .xml should be added to file
+ layoutMenu.add("Save as...").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ SingleDOM doc = multidom.getActiveDocument();
+ if (doc == null) {
+ JOptionPane.showMessageDialog(frame,
+ "No active document to save!",
+ "Save Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ JFileChooser fc = doc.getJFileChooser();
+ int rv = fc.showSaveDialog(frame);
+ if (rv != JFileChooser.APPROVE_OPTION) {
+ return;
+ }
+
+ File file = fc.getSelectedFile();
+ if (file.isDirectory()) { // this really should not happen?
+ JOptionPane.showMessageDialog(frame,
+ "Cannot save document as a directory!",
+ "Save Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // if file already exist prompt user
+ if (file.exists() && JOptionPane.showConfirmDialog(frame,
+ "Do you want to overwrite?", "Save...",
+ JOptionPane.YES_NO_OPTION) != 0) {
+ return;
+ }
+
+ try {
+ multidom.saveAsDocument(doc, file.getAbsolutePath());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+
+ layoutMenu.add("Export to ISOAgLib XML").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ SingleDOM doc = multidom.getActiveDocument();
+ if (doc == null) {
+ JOptionPane.showMessageDialog(frame,
+ "No active document to export!",
+ "Export Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ String testName = doc.getName().replace(".xml", "_export.xml");
+ JFileChooser fc = new JFileChooser();
+ fc.setSelectedFile(new File(testName));
+
+ // show dialog
+ int rv = fc.showSaveDialog(frame);
+ if (rv != JFileChooser.APPROVE_OPTION) {
+ return;
+ }
+
+ File file = fc.getSelectedFile();
+ if (file.isDirectory()) { // this really should not happen?
+ JOptionPane.showMessageDialog(frame,
+ "Cannot save document as a directory!",
+ "Save Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // if file already exist prompt user
+ if (file.exists() && JOptionPane.showConfirmDialog(frame,
+ "Do you want to overwrite?", "Save...", JOptionPane.YES_NO_OPTION) != 0) {
+ return;
+ }
+ try {
+ Tools.exportToXML1(file.getAbsolutePath(), doc.actual());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+
+ layoutMenu.add("Export to Embedded XML").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ SingleDOM doc = multidom.getActiveDocument();
+ if (doc == null) {
+ JOptionPane.showMessageDialog(frame,
+ "No active document to export!",
+ "Export Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ String testName = doc.getName().replace(".xml", "_export.xml");
+ JFileChooser fc = new JFileChooser( );
+ fc.setSelectedFile(new File(testName));
+
+ // show dialog
+ int rv = fc.showSaveDialog(frame);
+ if (rv != JFileChooser.APPROVE_OPTION) {
+ return;
+ }
+
+ File file = fc.getSelectedFile();
+ if (file.isDirectory()) { // this really should not happen?
+ JOptionPane.showMessageDialog(frame,
+ "Cannot save document as a directory!",
+ "Save Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // if file already exist prompt user
+ if (file.exists() && JOptionPane.showConfirmDialog(frame,
+ "Do you want to overwrite?", "Save...", JOptionPane.YES_NO_OPTION) != 0) {
+ return;
+ }
+ try {
+ Tools.exportToXML3(msgOutput, file.getAbsolutePath(), doc.actual());
+ } catch (Exception ex) {
+ JOptionPane.showMessageDialog(frame,
+ ex.getMessage(),
+ "Export Error", JOptionPane.ERROR_MESSAGE);
+ ex.printStackTrace();
+ }
+ }
+ });
+
+ layoutMenu.addSeparator();
+
+ layoutMenu.add("Exit").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (JOptionPane.showConfirmDialog(frame,
+ "Do you really want to exit?",
+ "Exit", JOptionPane.YES_NO_OPTION) == 0) {
+
+ // NOTE: what is the difference between System.exit(0)
+ // and this?
+ Runtime.getRuntime().exit(0);
+ }
+ }
+ });
+
+ /*
+ layoutMenu.add("Test").addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ try {
+ View view = createNewObjectTreeView(multidom.loadDocument(TEMPLATE));
+ final TabWindow twin = new TabWindow(view);
+ rootWindow.setWindow(new SplitWindow(true, 0.8f, rootWindow.getWindow(), twin));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+ */
+
+ return layoutMenu;
+ }
+
+ /**
+ * Creates the menu where layout can be saved and loaded.
+ * @return the layout menu
+ */
+ private JMenu createLayoutMenu() {
+ JMenu layoutMenu = new JMenu("Layout");
+
+ layoutMenu.add("Default Layout").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ setDefaultLayout();
+ }
+ });
+
+ layoutMenu.addSeparator();
+
+ for (int i = 0; i < layouts.length; i++) {
+ final int j = i;
+
+ layoutMenu.add("Save Layout " + i).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ // Save the layout in a byte array
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(bos);
+ rootWindow.write(out, false);
+ out.close();
+ layouts[j] = bos.toByteArray();
+ } catch (IOException e1) {
+ throw new RuntimeException(e1);
+ }
+ }
+ });
+ }
+
+ layoutMenu.addSeparator();
+
+ for (int i = 0; i < layouts.length; i++) {
+ final int j = i;
+
+ layoutMenu.add("Load Layout " + j).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (layouts[j] != null) {
+ try {
+ // Load the layout from a byte array
+ ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(layouts[j]));
+ rootWindow.read(in, true);
+ in.close();
+ } catch (IOException e1) {
+ throw new RuntimeException(e1);
+ }
+ }
+ }
+ });
+ }
+ });
+ }
+ return layoutMenu;
+ }
+
+ /**
+ * Creates the menu where views can be shown and focused.
+ * @return the focus view menu
+ */
+ private JMenu createFocusViewMenu() {
+ JMenu viewsMenu = new JMenu("Focus View");
+
+ for (int i = 0; i < views.length; i++) {
+ final View view = views[i];
+ viewsMenu.add("Focus " + view.getTitle()).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ // Ensure the view is shown in the root window
+ DockingUtil.addWindow(view, rootWindow);
+
+ // Transfer focus to the view
+ view.restoreFocus();
+ }
+ });
+ }
+ });
+ }
+ return viewsMenu;
+ }
+
+ /**
+ * Creates the menu where the theme can be changed.
+ * @return the theme menu
+ */
+ private JMenu createThemesMenu() {
+ JMenu themesMenu = new JMenu("Themes");
+
+ DockingWindowsTheme[] themes = {new DefaultDockingTheme(),
+ new BlueHighlightDockingTheme(),
+ new SlimFlatDockingTheme(),
+ new GradientDockingTheme(),
+ new ShapedGradientDockingTheme(),
+ new SoftBlueIceDockingTheme(),
+ new ClassicDockingTheme()};
+
+ ButtonGroup group = new ButtonGroup();
+
+ for (int i = 0; i < themes.length; i++) {
+ final DockingWindowsTheme theme = themes[i];
+
+ JRadioButtonMenuItem item = new JRadioButtonMenuItem(theme.getName());
+ item.setSelected(i == 4);
+ group.add(item);
+
+ themesMenu.add(item).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // Clear the modified properties values
+ properties.getMap().clear(true);
+
+ setTheme(theme);
+ }
+ });
+ }
+ return themesMenu;
+ }
+
+ /**
+ * Creates the menu where different property values can be modified.
+ * @return the properties menu
+ */
+ private JMenu createPropertiesMenu() {
+ JMenu buttonsMenu = new JMenu("Properties");
+
+ final JCheckBoxMenuItem hideClose = new JCheckBoxMenuItem("Hide Close Buttons");
+ final JCheckBoxMenuItem freezeLayout = new JCheckBoxMenuItem("Freeze Layout");
+
+ buttonsMenu.add(hideClose).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (hideClose.isSelected()) {
+ properties.getDockingWindowProperties().setCloseEnabled(false);
+ }
+ else if (!freezeLayout.isSelected()) {
+ properties.getDockingWindowProperties().setCloseEnabled(true);
+ }
+ }
+ });
+ buttonsMenu.add(freezeLayout).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (freezeLayout.isSelected()) {
+
+ // Disable window operations
+ properties.getDockingWindowProperties().setDragEnabled(false);
+ properties.getDockingWindowProperties().setCloseEnabled(false);
+ properties.getDockingWindowProperties().setMinimizeEnabled(false);
+ properties.getDockingWindowProperties().setRestoreEnabled(false);
+ properties.getDockingWindowProperties().setMaximizeEnabled(false);
+
+ // Enable tab reordering inside tabbed panel
+ properties.getTabWindowProperties().getTabbedPanelProperties().setTabReorderEnabled(true);
+ }
+ else {
+ // Enable window operations
+ properties.getDockingWindowProperties().setDragEnabled(true);
+ if (!hideClose.isSelected()) {
+ properties.getDockingWindowProperties().setCloseEnabled(true);
+ }
+ properties.getDockingWindowProperties().setMinimizeEnabled(true);
+ properties.getDockingWindowProperties().setRestoreEnabled(true);
+ properties.getDockingWindowProperties().setMaximizeEnabled(true);
+
+ // Disable tab reordering inside tabbed panel
+ properties.getTabWindowProperties().getTabbedPanelProperties().setTabReorderEnabled(false);
+ }
+ }
+ });
+ return buttonsMenu;
+ }
+
+ /**
+ * Creates the menu where individual window bars can be enabled and disabled.
+ * @return the window bar menu
+ */
+ private JMenu createWindowBarsMenu() {
+ JMenu barsMenu = new JMenu("Window Bars");
+
+ for (int i = 0; i < 4; i++) {
+ final Direction d = Direction.getDirections()[i];
+ JCheckBoxMenuItem item = new JCheckBoxMenuItem("Toggle " + d);
+ item.setSelected(d == Direction.DOWN);
+ barsMenu.add(item).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // Enable/disable the window bar
+ rootWindow.getWindowBar(d).setEnabled(!rootWindow.getWindowBar(d).isEnabled());
+ }
+ });
+ }
+ return barsMenu;
+ }
+
+ /**
+ * Creates the menu where not shown views can be shown.
+ * @return the view menu
+ */
+ private JMenu createViewMenu() {
+ JMenu menu = new JMenu("Views");
+ for (int i = 0; i < views.length; i++) {
+ final View view = views[i];
+ viewItems[i] = new JMenuItem(view.getTitle());
+ viewItems[i].setEnabled(views[i].getRootWindow() == null);
+ menu.add(viewItems[i]).addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (view.getRootWindow() != null)
+ view.restoreFocus();
+ else {
+ DockingUtil.addWindow(view, rootWindow);
+ }
+ }
+ });
+ }
+ return menu;
+ }
+
+ /**
+ * Sets the docking windows theme.
+ * @param theme the docking windows theme
+ */
+ private void setTheme(DockingWindowsTheme theme) {
+ properties.replaceSuperObject(currentTheme.getRootWindowProperties(),
+ theme.getRootWindowProperties());
+ currentTheme = theme;
+ }
+
+ /**
+ * Main method.
+ * @param args
+ * @throws java.lang.Exception
+ */
+ public static void main(String[] args) throws Exception {
+ // Set InfoNode Look and Feel
+ UIManager.setLookAndFeel(new InfoNodeLookAndFeel());
+
+ // Docking windows should be run in the Swing thread
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ new Main();
+ }
+ });
+ }
+
+ /**
+ * Creates the help menu.
+ * @return
+ */
+ private JMenu createHelpMenu() {
+ final JMenu menu = new JMenu("Help");
+ menu.add("About").addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ JOptionPane.showMessageDialog(frame,
+ new String[] {
+ "PoolEdit",
+ "Version 1.4",
+ " ",
+ "Copyright (C) 2007 Automation technology laboratory,",
+ "Helsinki University of Technology",
+ " ",
+ "Visit automation.tkk.fi for information about the automation",
+ "technology laboratory.",
+ " ",
+ "This program is free software; you can redistribute it and/or",
+ "modify it under the terms of the GNU General Public License",
+ "as published by the Free Software Foundation; either version",
+ "3 of the License, or (at your option) any later version.",
+ " ",
+ "Authors:",
+ "Matti Öhman (mohman@cc.hut.fi)",
+ "Jouko Kalmari"},
+ "About PoolEdit", JOptionPane.INFORMATION_MESSAGE, Icons.POOLEDIT_LOGO);
+ }
+ });
+ return menu;
+ }
+}
diff --git a/src/pooledit/PictureConverter.java b/src/pooledit/PictureConverter.java
new file mode 100644
index 0000000..22b1bee
--- /dev/null
+++ b/src/pooledit/PictureConverter.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package pooledit;
+
+import color.ColorPalette;
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+
+/**
+ *
+ * @author Autlab
+ */
+public class PictureConverter {
+
+ /**
+ * Creates a new instance of PictureConverter
+ */
+ private PictureConverter() {
+ }
+
+ /**
+ * Converts the specified image to base64 encoded string, which is the
+ * embedded XML image format.
+ */
+ public static String convertToString(BufferedImage image) {
+ int width = image.getWidth();
+ int height = image.getHeight();
+ int pictureData[] = new int[width * height];
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ pictureData[y * width + x] = ColorPalette.getPaletteIndex(image.getRGB(x, y));
+ }
+ }
+ char[] base64 = convertToBase64(pictureData);
+
+ return new String(base64);
+ }
+
+ public static BufferedImage applyTransparencyAndReduceColors(BufferedImage image,
+ Color transparencyColor, boolean transparent,
+ boolean reduceImages, int colorDepth) {
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+ if (transparent) {
+ BufferedImage image2 = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (image.getRGB(x, y) == transparencyColor.getRGB()) {
+ image2.setRGB(x, y, 0);
+ }
+ else if (reduceImages) {
+ image2.setRGB(x, y, 0xFF << 24 | ColorPalette.getColorRGB(image.getRGB(x, y), colorDepth));
+ }
+ else {
+ image2.setRGB(x, y, image.getRGB(x, y));
+ }
+ }
+ }
+ image = image2;
+ }
+ else if (reduceImages) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ image.setRGB(x, y, ColorPalette.getColorRGB(image.getRGB(x, y), colorDepth));
+ }
+ }
+ }
+ return image;
+ }
+
+ /*
+ public static void reduceColorsToPalette(BufferedImage image, int colorDepth) {
+ for (int y = 0, height = image.getHeight(); y < height; y++) {
+ for (int x = 0, width = image.getWidth(); x < width; x++) {
+ image.setRGB(x, y, ColorPalette.getColorRGB(image.getRGB(x, y), colorDepth));
+ }
+ }
+ System.out.println("CONVERTING");
+ }
+ */
+ /*
+ public static BufferedImage reduceColorsToPalette(BufferedImage image, int colorDepth) {
+ int width = image.getWidth();
+ int height = image.getHeight();
+ BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ output.setRGB(x, y, ColorPalette.getColorRGB(image.getRGB(x, y), colorDepth));
+ //output.setRGB(x, y, ColorPalette.getNearestColor(image.getRGB(x, y)));
+ }
+ }
+ System.out.println("CONVERTING");
+ return output;
+ }
+ */
+
+ /**
+ * Returns the character that is equalent to given index.
+ * e.g. index=0 -> returns A
+ */
+ public static char indexToBase64(int index) {
+ if (index >= 0 && index < 26) {
+ return (char) ('A' + index);
+ }
+ else if (index >= 26 && index < 52) {
+ return (char) ('a' - 26 + index);
+ }
+ else if (index >= 52 && index < 62) {
+ return (char) ('0' - 52 + index);
+ }
+ else if (index == 62) {
+ return '+';
+ }
+ else if (index == 63) {
+ return '/';
+ }
+ else {
+ throw new IllegalArgumentException("The value of the index should " +
+ "be [0, 63] (" + index + ")");
+ }
+ }
+
+ //converts data to base64, data must be bytes!!
+ public static char[] convertToBase64(int data[]) {
+ int length64 = (data.length * 4 + 2) / 3;
+ char base64[] = new char[length64];
+
+ for (int i = 0; i < length64; i++) {
+ int highI = i * 3 / 4;
+ int lowI = (i + 1) * 3 / 4;
+ int high = data[highI];
+ int low = lowI < data.length ? data[lowI] : 0; // pad with zeros
+ int index;
+ switch (i % 4) {
+ case 0: // [H7][H6][H5][H4][H3][H2]
+ index = high >> 2;
+ break;
+ case 1: // [H1][H0][L7][L6][L5][L4]
+ index = ((high & 0x03) << 4) + (low >> 4);
+ break;
+ case 2: // [H3][H2][H1][H0][L7][L6]
+ index = ((high & 0x0F) << 2) + (low >> 6);
+ break;
+ default: // [H5][H4][H3][H2][H1][H0]
+ index = high & 0x3F;
+ break;
+ }
+ base64[i] = indexToBase64(index);
+ }
+ return base64;
+ }
+
+ /************************* Convert back **************************/
+
+ public static int base64toIndex(char base64) {
+ if (base64 >= 'A' && base64 <= 'Z') {
+ return base64 - 'A';
+ }
+ else if (base64 >= 'a' && base64 <= 'z') {
+ return base64 - 'a' + 26;
+ }
+ else if (base64 >= '0' && base64 <= '9') {
+ return base64 - '0' + 52;
+ }
+ else if (base64 == '+') {
+ return 62;
+ }
+ else if (base64 == '/') {
+ return 63;
+ }
+ else {
+ throw new IllegalArgumentException("The value of the base64 should " +
+ "be a valid character (" + base64 + ")");
+ }
+ }
+
+ public static int[] convertFromBase64(char base64[]) {
+ int length = base64.length * 3 / 4;
+ int data[] = new int[length];
+
+ for (int i = 0; i < length; i++) {
+ int index = i * 4 / 3;
+ int high = base64toIndex(base64[index]);
+ int low = base64toIndex(base64[index + 1]);
+
+ switch (i % 3) {
+ case 0: // [H5][H4][H3][H2][H1][H0][L5][L4]
+ data[i] = (high << 2) + (low >> 4);
+ break;
+ case 1: // [H3][H2][H1][H0][L5][L4][L3][L2]
+ data[i] = ((high & 0x0F) << 4) + (low >> 2);
+ break;
+ default: // [H1][H0][L5][L4][L3][L2][L1][L0]
+ data[i] = ((high & 0x03) << 6) + low;
+ break;
+ }
+ }
+ return data;
+ }
+}
diff --git a/src/pooledit/PoolException.java b/src/pooledit/PoolException.java
new file mode 100644
index 0000000..0e1c15e
--- /dev/null
+++ b/src/pooledit/PoolException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package pooledit;
+
+/**
+ *
+ * @author mohman
+ */
+public class PoolException extends Exception {
+
+ /** Constructs a new exception with null as its detail message. */
+ public PoolException() {
+ }
+
+ /** Constructs a new exception with the specified detail message. */
+ public PoolException(String message) {
+ super(message);
+ }
+
+ /** Constructs a new exception with the specified detail message and cause. */
+ public PoolException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public PoolException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/pooledit/Tools.java b/src/pooledit/Tools.java
new file mode 100644
index 0000000..735c2b6
--- /dev/null
+++ b/src/pooledit/Tools.java
@@ -0,0 +1,1889 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package pooledit;
+
+import static pooledit.Definitions.*;
+import attributetable.AttributeTable;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.LinkedList;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeSet;
+import javax.imageio.ImageIO;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import org.w3c.dom.Attr;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.bootstrap.DOMImplementationRegistry;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSOutput;
+import org.w3c.dom.ls.LSParser;
+import org.w3c.dom.ls.LSSerializer;
+
+/**
+ *
+ * @author mohman
+ */
+public class Tools {
+
+ private static final DOMImplementationRegistry REGISTRY;
+ private static final DOMImplementationLS IMPL_LS;
+ static {
+ System.setProperty(DOMImplementationRegistry.PROPERTY,
+ "org.apache.xerces.dom.DOMImplementationSourceImpl");
+ DOMImplementationRegistry reg = null;
+ try {
+ reg = DOMImplementationRegistry.newInstance();
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ System.exit(-1);
+ }
+ REGISTRY = reg;
+ IMPL_LS = (DOMImplementationLS) REGISTRY.getDOMImplementation("LS 3.0");
+ }
+
+ /**
+ * Creates a (root level) name map of all real objects that have a name
+ * attribute. The key is the name value of attribute and value is the
+ * element.
+ */
+ public static Map createNameMap(Document doc) {
+ // create a new HashMap
+ return updateNameMap(doc, new HashMap(), false);
+ }
+
+ public static Map createNameMap(Document doc, boolean fullMap) {
+ // create a new HashMap
+ return updateNameMap(doc, new HashMap(), fullMap);
+ }
+
+ /**
+ * Updates (root level) name map.
+ */
+ public static Map updateNameMap(Document doc,
+ Map nameMap) {
+ return updateNameMap(doc, nameMap, false);
+ }
+
+ public static Map updateNameMap(Document doc,
+ Map nameMap, boolean fullMap) {
+
+ nameMap.clear();
+ // get all elements in the order they appear in the document
+ NodeList nodes = fullMap ? doc.getDocumentElement().getElementsByTagName("*") :
+ doc.getDocumentElement().getChildNodes();
+
+ // iterate over all elements
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Node node = nodes.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element element = (Element) node;
+
+ // is interesting and has a name?
+ if (Utils.equals(element.getTagName(), OBJECTS)) {
+
+ String name = element.getAttribute(NAME);
+ if (!name.isEmpty()) {
+
+ // use simple structured names for objects with language
+ // attribute
+ String language = element.getAttribute(LANGUAGE);
+ if (!language.isEmpty() && !language.equals("en")) {
+ name += "." + language;
+ }
+
+ // do not complain if building a full map
+ if (!fullMap && nameMap.containsKey(name)) {
+ System.err.println(Tools.class.getName() + " name \"" +
+ name + "\" is already defined!");
+ }
+ // add to map
+ nameMap.put(name, element);
+ }
+ }
+ }
+ }
+ return nameMap;
+ }
+
+ /**
+ * Creates a name-attribute (and name) to all objects that should
+ * have it and don't. The name map is updated.
+ *
+ * @attribute doc DOM-model of document
+ * @attribue nameMap the HashMap
+ public static void createMissingNames(Element element,
+ Map nameMap) {
+
+ // get all elements in the order they appear in the document
+ NodeList elements = element.getElementsByTagName("*");
+
+ // iterate over all elements
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Element elem = (Element) elements.item(i);
+
+ // missing a name?
+ if (equals(elem.getTagName(), OBJECTS) &&
+ elem.getAttribute(NAME).isEmpty()) {
+
+ // find free name, set it, and update name map
+ String name = findFreeName(elem.getTagName(), nameMap);
+ elem.setAttribute(NAME, name);
+ nameMap.put(name, elem);
+ }
+ }
+ }
+ */
+
+ /**
+ * Must be executed after createRoles!
+ */
+ public static void checkNaming(Document doc) {
+
+ // full name map contains all the names in the document
+ Map fullMap = createNameMap(doc, true);
+
+ // root name map contains only the names defined at the root level
+ Map rootMap = createNameMap(doc, false);
+
+ // nodes that need fixing are put on this work list
+ List worklist = new ArrayList();
+
+ Element root = doc.getDocumentElement();
+
+ // iterate over all references
+ NodeList elements = root.getElementsByTagName(INCLUDE_OBJECT);
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Element elem = (Element) elements.item(i);
+ String name = elem.getAttribute(NAME);
+ if (fullMap.containsKey(name)) {
+ if (!rootMap.containsKey(name)) {
+ System.err.println(name + " (in " + ((Element) elem.getParentNode()).getAttribute(NAME) +
+ ") refers to an element that is not on the root level!");
+
+ Element referenced = fullMap.get(name);
+ rootMap.put(name, referenced);
+ worklist.add(referenced);
+ }
+ }
+ else {
+ System.err.println(name + " refers to an element that does not exist!");
+ }
+ }
+
+ // work through the list, replace the problematic nodes with links
+ // and move actual nodes to the root level
+ for (Element ref : worklist) {
+ Element link = doc.createElement(INCLUDE_OBJECT);
+ copyAttributes(ref, link, NAME, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+ removeAttributes(ref, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+ ref.getParentNode().replaceChild(link, ref);
+ root.appendChild(ref);
+ }
+ }
+
+ /**
+ * Finds the next unused name, if there is number in the back it's
+ * used
+ *
+ * @param name name to be examined
+ * @param nameMap HashMap including all used names
+ * @return next free name
+ *
+ * example: name="asdf2" and asdf2 and asdf3 are used
+ * result asdf4
+ */
+ public static String findFreeName(String name,
+ Map nameMap) {
+
+ // separate name into basename and end digits
+ int i = name.length() - 1;
+ while (i >= 0 && Character.isDigit((name.charAt(i)))) i--;
+ String basename = name.substring(0, i + 1);
+ String digits = name.substring(i + 1);
+ int n = digits.equals("") ? 0 : Integer.parseInt(digits);
+
+ //go through names until free name is found
+ String result;
+ while (nameMap.containsKey(result = basename + n)) n++;
+ return result;
+ }
+
+ /**
+ * Checks if the document and nameMap are ok
+ public static boolean checkDocument(Document doc,
+ Map nameMap) {
+
+ // get all elements in the order they appear in the document
+ NodeList elements = doc.getDocumentElement().getElementsByTagName("*");
+ boolean ok = true;
+
+ // iterate over all elements
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Element element = (Element) elements.item(i);
+ if (element.getNodeName().equals("include_object")) {
+ if (!element.hasAttribute("name") ||
+ !nameMap.containsKey(element.getAttribute("name"))) {
+ System.out.println("Error, missing node: " +
+ element.getAttribute("name"));
+ ok = false;
+ }
+ }
+ }
+ return ok;
+ }
+ */
+
+ public static Document parseDocument(String text, String schema) {
+ LSParser parser =
+ IMPL_LS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS,
+ "http://www.w3.org/2001/XMLSchema");
+
+ DOMConfiguration config = parser.getDomConfig();
+ config.setParameter("validate", Boolean.TRUE);
+ config.setParameter("schema-type", "http://www.w3.org/2001/XMLSchema");
+ config.setParameter("schema-location", schema);
+
+ LSInput input = IMPL_LS.createLSInput();
+ input.setStringData(text);
+ return parser.parse(input);
+
+ }
+
+ public static Document loadDocument(String name, String schema) {
+ LSParser parser =
+ IMPL_LS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS,
+ "http://www.w3.org/2001/XMLSchema");
+
+ DOMConfiguration config = parser.getDomConfig();
+ config.setParameter("validate", Boolean.TRUE);
+ config.setParameter("schema-type", "http://www.w3.org/2001/XMLSchema");
+ config.setParameter("schema-location", schema);
+
+ //config.setParameter("element-content-whitespace", Boolean.FALSE);
+
+ //???? doesn't work
+ //DOMErrorHandlerImpl errorHandler = new DOMErrorHandlerImpl();
+ //config.setParameter("error-handler", errorHandler);
+
+ //config.setParameter("validate", Boolean.TRUE);
+ //config.setParameter("schema-type",
+ //"http://www.w3.org/2001/XMLSchema");
+ //config.setParameter("validate-if-schema", Boolean.TRUE);
+ //config.setParameter("schema-location", "catalog.xsd");
+ return parser.parseURI(name);
+ }
+
+ public static void saveDocument(String name, Document doc)
+ throws FileNotFoundException, IOException {
+ LSSerializer dom3Writer = IMPL_LS.createLSSerializer();
+ DOMConfiguration config = dom3Writer.getDomConfig();
+ config.setParameter("format-pretty-print", Boolean.TRUE);
+
+ OutputStream outputStream = new FileOutputStream(new File(name));
+ LSOutput output = IMPL_LS.createLSOutput();
+ output.setEncoding("UTF-8");
+ output.setByteStream(outputStream);
+ dom3Writer.write(doc, output);
+ outputStream.close(); // remember to close the output stream!
+ }
+
+ /**
+ * Exports the document using the XML format specified in ISOAbLib.
+ *
+ * - roles are converted back to attributes
+ *
- start and end angles are converted to "double degrees" (divided by two)
+ *
+ */
+ public static void exportToXML1(String name, Document doc)
+ throws FileNotFoundException, IOException {
+ // the whole document is cloned (this copies all attributes too!)
+ Document clone = (Document) doc.cloneNode(true);
+
+ removeRoles(clone.getDocumentElement());
+ divAngles(clone.getDocumentElement());
+
+ // remove empty attributes? (at least file1 file4 file8... ?)
+
+ LSSerializer dom3Writer = IMPL_LS.createLSSerializer();
+ DOMConfiguration config = dom3Writer.getDomConfig();
+ config.setParameter("format-pretty-print", Boolean.TRUE);
+
+ OutputStream outputStream = new FileOutputStream(new File(name));
+ LSOutput output = IMPL_LS.createLSOutput();
+ output.setEncoding("UTF-8");
+ output.setByteStream(outputStream);
+ dom3Writer.write(clone, output);
+ outputStream.close(); // remember to close the output stream!
+ }
+
+ /**
+ * Exports the document using a stand-alone XML format.
+ *
+ * - images are embedded into the XML using base64 encoding
+ *
- missing ids are generated for objects
+ *
- id attributes are added to include_objects
+ *
+ */
+ public static void exportToXML3(PrintStream out, String fileName, Document doc) throws IOException, PoolException {
+
+ // the document is cloned (this copies all attributes too!)
+ Document clone = (Document) doc.cloneNode(true);
+
+ // check the validity of document
+ if (validateDocument(out, clone)) {
+ out.println("Document valid");
+ }
+ else {
+ out.println("Document invalid!");
+ }
+
+ divAngles(clone.getDocumentElement()); //This could be done?
+
+ //createRoles(clone.getDocumentElement()); //this shouldn't be necessary !!!
+
+ // find all picture elements
+ NodeList nodes = clone.getElementsByTagName(PICTUREGRAPHIC);
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Element element = (Element) nodes.item(i);
+
+ // get image and convert it to base64
+ BufferedImage image = getImageFile(element, FILE, getStdBitmapPath(doc));
+
+ // remove attributes that are no longer needed
+ removeAttributes(element, FILE, FILE1, FILE4, FILE8, FORMAT, RLE);
+
+ // remove all children
+ NodeList childs = element.getChildNodes();
+ for (int j = childs.getLength() - 1; j >= 0; j--) {
+ element.removeChild(childs.item(j));
+ }
+
+ // create image data element
+ Element dataElement = clone.createElement(IMAGE_DATA);
+
+ // add image_width and image_height attributes
+ dataElement.setAttribute(IMAGE_WIDTH, Integer.toString(image.getWidth()));
+ dataElement.setAttribute(IMAGE_HEIGHT, Integer.toString(image.getHeight()));
+
+ // set content
+ dataElement.setTextContent(PictureConverter.convertToString(image));
+ element.appendChild(dataElement);
+ }
+
+ // set output string lengths
+ NodeList outputstrings = clone.getElementsByTagName(OUTPUTSTRING);
+ for (int i = 0, n = outputstrings.getLength(); i < n; i++) {
+ Element outputstring = (Element) outputstrings.item(i);
+ String value = outputstring.getAttribute(VALUE);
+ int valLen = value.length();
+ String length = outputstring.getAttribute(LENGTH);
+
+ // FIXME: what is this for?
+ if (length.equals("")) {
+ out.println(outputstring.getAttribute(NAME));
+ }
+
+ int len = 0;
+ try {
+ len = Integer.parseInt(length);
+ }
+ catch (NumberFormatException ex) {
+ // do nothing
+ }
+ if (valLen > len) {
+ outputstring.setAttribute(LENGTH, Integer.toString(valLen));
+ }
+ }
+
+ // set output string lengths
+ NodeList inputstrings = clone.getElementsByTagName(INPUTSTRING);
+ for (int i = 0, n = inputstrings.getLength(); i < n; i++) {
+ Element inputstring = (Element) inputstrings.item(i);
+ String value = inputstring.getAttribute(VALUE);
+ int valLen = value.length();
+ String length = inputstring.getAttribute(LENGTH);
+ int len = Integer.parseInt(length);
+ if (valLen > len) {
+ inputstring.setAttribute(LENGTH, Integer.toString(valLen));
+ }
+ }
+
+ // create id to all objects
+ nodes = clone.getElementsByTagName("*");
+ TreeSet usedIDs = new TreeSet();
+
+ // find all ids and remove id from include_objects
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Element element = (Element) nodes.item(i);
+ String id = element.getAttribute(ID);
+ if (!id.isEmpty()) {
+ if (element.getNodeName().equals(INCLUDE_OBJECT)) {
+ element.removeAttribute(ID);
+ out.println("Removing ID " + id + " from " + element.getAttribute(NAME) + " (include_object)");
+ }
+ else {
+ Integer idn = new Integer(id); //Integer.getInteger(id);
+ if (idn == null || usedIDs.contains(idn)) {
+ element.removeAttribute(ID);
+ out.println("ID " + idn + " already in use. Removing id from " + element.getAttribute(NAME) +
+ " (" + element.getNodeName() + ")");
+ }
+ else {
+ usedIDs.add(idn);
+ out.println("Reserving ID " + idn + " for " + element.getAttribute(NAME) +
+ " (" + element.getNodeName() + ")");
+ }
+ }
+ }
+ }
+
+ // find all real elements with no id and them a unused id
+ int nextMacroId = 0;
+ int nextId = 256;
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Element element = (Element) nodes.item(i);
+ String id = element.getAttribute("id");
+ // if a real isobus objects has no id, then create it one
+ if (id.isEmpty() && Utils.equals(element.getNodeName(), OBJECTS)) {
+
+ // macro ids are [0..255]
+ if (element.getNodeName().equals(MACRO)) {
+ while (usedIDs.contains(nextMacroId)) nextMacroId++;
+ if (nextMacroId >= 256) {
+ throw new PoolException("Too many macros (>256)!");
+ }
+ element.setAttribute(ID, Integer.toString(nextMacroId));
+ usedIDs.add(nextMacroId);
+ out.println("Created ID " + nextMacroId + " for " + element.getAttribute(NAME) +
+ " (" + element.getNodeName() + ")");
+ }
+ // other ids are [256..65535]
+ else {
+ while (usedIDs.contains(nextId)) nextId++;
+ if (nextMacroId >= 65536) {
+ throw new PoolException("Too many objects!");
+ }
+ element.setAttribute(ID, Integer.toString(nextId));
+ usedIDs.add(nextId);
+ out.println("Created ID "+ nextId + " for " + element.getAttribute(NAME) +
+ " (" + element.getNodeName() + ")");
+ }
+ }
+ }
+
+ // use name map to add right id to all include_objects
+ Map nameMap = Tools.createNameMap(clone);
+ nodes = clone.getElementsByTagName(INCLUDE_OBJECT);
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Element element = (Element) nodes.item(i);
+ String name = element.getAttribute(NAME);
+ if (nameMap.containsKey(name)) {
+ element.setAttribute("id", nameMap.get(name).getAttribute("id"));
+ }
+ else {
+ out.println("ERROR: Can't find object: \"" + name + "\"");
+ }
+ }
+
+ // replace all block_font-attributes with block_font_size
+ nodes = clone.getElementsByTagName("*");
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Element element = (Element) nodes.item(i);
+ String blockFont = element.getAttribute(BLOCK_FONT);
+ if (!blockFont.isEmpty()){
+ if (nameMap.containsKey(blockFont)) {
+ Element font = nameMap.get(blockFont);
+ element.setAttribute(BLOCK_FONT_SIZE, font.getAttribute(FONT_SIZE));
+ }
+ element.removeAttribute(BLOCK_FONT);
+ }
+ else {
+ // if no block font then these are not needed
+ element.removeAttribute(BLOCK_COL);
+ element.removeAttribute(BLOCK_ROW);
+ element.removeAttribute(BLOCK_FONT);
+ }
+ }
+
+ nodes = clone.getElementsByTagName("*");
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Element element = (Element) nodes.item(i);
+ String name = element.getNodeName();
+ if (Utils.equals(name, DATAMASK, ALARMMASK)) {
+ markChildrenMask(out, element, nameMap);
+ }
+ // marking softkeymask is necessary as it can contain pointers as well as keys
+ else if (Utils.equals(name, WORKINGSET, SOFTKEYMASK, KEY, AUXILIARYFUNCTION, AUXILIARYINPUT)) {
+ markChildrenDesignator(out, element, nameMap);
+ }
+ }
+
+ // FIXME: add an extra pass to find out unreferenced elements?
+
+ /*
+ // DIDN'T WORK TOO WELL, PROBLEMS WITH SPECIAL RELATIONS E.G. activemask, etc.
+ // generate missing use attributes
+ List list = new ArrayList();
+ nodes = clone.getElementsByTagName("*");
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Element element = (Element) nodes.item(i);
+ String name = element.getNodeName();
+ if (!equals(name, OBJECTS)) {
+ continue;
+ }
+ list.clear();
+ list.add(element); // if the element is e.g. working set its use should be designator
+ findAncestorElements(element, list);
+ boolean mask = containsElements(list, DATAMASK, ALARMMASK);
+ boolean designator = containsElements(list, WORKINGSET, KEY, AUXILIARYFUNCTION, AUXILIARYINPUT);
+
+ // special case: even if datamask is used in working set as "activemask"
+ // it does not mean it is used inside a designator!
+ if (equals(name, DATAMASK, ALARMMASK)) {
+ designator = false;
+ }
+ // special case: even if softkeymask is used in datamask as "softkeymask"
+ // it does not mean it is used inside a mask!
+ if (equals(name, SOFTKEYMASK)) {
+ mask = false;
+ }
+ if (mask && designator) {
+ if (equals(element.getAttribute(USE), "both")) {
+ System.out.println("WARNING: element \"" + element.getAttribute(NAME) + "\" (" + name +
+ ") use attribute is \"both\"!");
+ }
+ else {
+ System.err.println("ERROR: element \"" + element.getAttribute(NAME) + "\" (" + name +
+ ") is used both in mask and designator!");
+ }
+ element.setAttribute(USE, "both");
+ }
+ else if (mask) {
+ if (!equals(element.getAttribute(USE), "", "mask")) {
+ System.out.println("WARNING: element \"" + element.getAttribute(NAME) + "\" (" + name +
+ ") use attribute is overriden to \"mask\"!");
+ }
+ element.setAttribute(USE, "mask");
+ }
+ else if (designator) {
+ if (!equals(element.getAttribute(USE), "", "designator")) {
+ System.out.println("WARNING: element \"" + element.getAttribute(NAME) + "\" (" + name +
+ ") use attribute is overriden to \"designator\"!");
+ }
+ element.setAttribute(USE, "designtor");
+ }
+ else {
+ System.out.println("WARNING: element \"" + element.getAttribute(NAME) + "\" (" + name +
+ ") is not used in mask or designator (please specify \"mask\" or \"designator\" manually)!");
+ }
+ }
+ */
+
+ // write to file
+ LSSerializer dom3Writer = IMPL_LS.createLSSerializer();
+ DOMConfiguration config = dom3Writer.getDomConfig();
+ config.setParameter("format-pretty-print", Boolean.TRUE);
+
+ OutputStream outputStream = new FileOutputStream(new File(fileName));
+ LSOutput output = IMPL_LS.createLSOutput();
+ output.setEncoding("UTF-8");
+ output.setByteStream(outputStream);
+ dom3Writer.write(clone, output);
+ outputStream.close(); // remember to close the output stream!
+ }
+
+ private static void markChildrenMask(PrintStream out, Element element, Map nameMap) {
+ String type = element.getNodeName();
+ String name = element.getAttribute(NAME);
+ String use = element.getAttribute(USE);
+ if (Utils.equals(use, "designator", "both") &&
+ !Utils.equals(type, FILLATTRIBUTES)) {
+ out.println("ERROR: element \"" + name + "\" (" + type +
+ ") use attribute is now \"both\"!");
+ element.setAttribute(USE, "both");
+ }
+ else {
+ element.setAttribute(USE, "mask");
+ }
+ List children = getChildElementList(element);
+ for (Element e : children) {
+ // follow links
+ if (Utils.equals(e.getNodeName(), INCLUDE_OBJECT)) {
+ e = nameMap.get(e.getAttribute(NAME));
+ }
+ // skip if child is not a proper object or if
+ // the element is data / alarmmask and the child is
+ // softkeymask
+ String childType = e.getNodeName();
+ if (!Utils.equals(childType, OBJECTS) ||
+ (Utils.equals(type, DATAMASK, ALARMMASK) &&
+ Utils.equals(childType, SOFTKEYMASK))) {
+ continue;
+ }
+ markChildrenMask(out, e, nameMap);
+ }
+ }
+
+ private static void markChildrenDesignator(PrintStream out, Element element, Map nameMap) {
+ String type = element.getNodeName();
+ String name = element.getAttribute(NAME);
+ String use = element.getAttribute(USE);
+ if (Utils.equals(use, "mask", "both") &&
+ !Utils.equals(type, FILLATTRIBUTES)) {
+ out.println("ERROR: element \"" + name + "\" (" + type +
+ ") use attribute is now \"both\"!");
+ element.setAttribute(USE, "both");
+ }
+ else {
+ element.setAttribute(USE, "designator");
+ }
+ List children = getChildElementList(element);
+ for (Element e : children) {
+ // follow links
+ if (Utils.equals(e.getNodeName(), INCLUDE_OBJECT)) {
+ e = nameMap.get(e.getAttribute(NAME));
+ }
+ // skip if child is not a proper object or if
+ // the element is workingset and the child is
+ // data / alarmmask
+ String childType = e.getNodeName();
+ if (!Utils.equals(childType, OBJECTS) ||
+ (Utils.equals(type, WORKINGSET) &&
+ Utils.equals(childType, DATAMASK, ALARMMASK))) {
+ continue;
+ }
+ markChildrenDesignator(out, e, nameMap);
+ }
+ }
+
+ /**
+ * Check the given list of elements for the specified element names.
+ */
+ private static boolean containsElements(List list, String ... names) {
+ for (Element e : list) {
+ if (Utils.equals(e.getNodeName(), names)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if all attributes, included objects are ok
+ */
+ public static boolean validateDocument(PrintStream out, Document doc) {
+ ByteArrayOutputStream bff = new ByteArrayOutputStream();
+ PrintStream tmp = new PrintStream(bff);
+ boolean ok = true;
+ Map nameMap = Tools.createNameMap( doc );
+
+ // find all real isobus-elements
+ NodeList elements = doc.getElementsByTagName("*");
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Element element = (Element) elements.item(i);
+
+ // validate element roles
+ if (Utils.equals(element.getNodeName(), OBJECTS)) {
+ if (!validateElementRoles(tmp, element, nameMap)) {
+ out.println(getPath(element) + " (" + element.getNodeName() + ")");
+ out.print(bff);
+ ok = false;
+ }
+ }
+ }
+ // FIXME: check that the child objects are right?
+ return ok;
+ }
+
+ public static String getPath(Element element) {
+ String path = element.getAttribute(NAME);
+ Node node = element;
+ while ((node = node.getParentNode()) != null) {
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ path = ((Element) node).getAttribute(NAME) + "/" + path;
+ }
+ else {
+ break;
+ }
+ }
+ return path;
+ }
+
+
+ private static final String[] ELEMENTS_WITH_ROLES =
+ {DATAMASK, ALARMMASK, SOFTKEYMASK,
+ NUMBERVARIABLE, STRINGVARIABLE, FONTATTRIBUTES,
+ LINEATTRIBUTES, FILLATTRIBUTES, INPUTATTRIBUTES};
+
+ /**
+ * Checks that the role attribute of the given element is appropriate
+ * in the current context of the element.
+ *
+ * If the element is a link, it is substituted with the actual object.
+ * In principle, these kinds of check should be made using XML Schema,
+ * but linking complicates things.
+ */
+ private static boolean validateElementRoles(PrintStream out,
+ Element element, Map nameMap) {
+
+ Element linkedElement = element;
+ if (element.getNodeName().equals(INCLUDE_OBJECT)) {
+ linkedElement = nameMap.get(element.getAttribute(NAME));
+ if (linkedElement == null) {
+ out.println("\tbroken link!");
+ return false;
+ }
+ }
+
+ String linkedElementName = linkedElement.getNodeName();
+ Element parent = (Element) element.getParentNode();
+
+ //check if elment is a attribute or variable element
+
+ if (Utils.equals(linkedElementName, ELEMENTS_WITH_ROLES)) {
+
+ String role = element.getAttribute(ROLE); //role is in the link!!
+
+ // if the element is on the root level, it can't have a role
+ if (parent.getNodeName().equals(OBJECTPOOL)) {
+ if (!Utils.equals(role, "", "none")) {
+ out.println("\tillegal role (" + role + ")!");
+ return false;
+ }
+ }
+ // if the element is under any other object, it must have the right role
+ else {
+ if (Utils.equals(linkedElementName, DATAMASK, ALARMMASK)) {
+ if (!Utils.equals(role, ACTIVE_MASK)) {
+ out.println("\tillegal role! (" + role + ")!");
+ return false;
+ }
+ }
+ else if (Utils.equals(linkedElementName, SOFTKEYMASK)) {
+ if (!Utils.equals(role, SOFT_KEY_MASK)) {
+ out.println("\tillegal role! (" + role + ")!");
+ return false;
+ }
+ }
+ else if (linkedElementName.equals(NUMBERVARIABLE)) {
+ if (!Utils.equals(role, VARIABLE_REFERENCE, TARGET_VALUE_VARIABLE_REFERENCE)) {
+ out.println("\tillegal role! (" + role + ")!");
+ return false;
+ }
+ }
+ else if (linkedElementName.equals(STRINGVARIABLE)) {
+ if (!Utils.equals(role, VARIABLE_REFERENCE)) {
+ out.println("\tillegal role (" + role + ")!");
+ return false;
+ }
+ }
+ else if (linkedElementName.equals(FONTATTRIBUTES)) {
+ if (!Utils.equals(role, FONT_ATTRIBUTES, FOREGROUND_COLOUR)) {
+ out.println("\tillegal role (" + role + ")!");
+ return false;
+ }
+ }
+ else if (linkedElementName.equals(LINEATTRIBUTES)) {
+ if (!Utils.equals(role, LINE_ATTRIBUTES)) {
+ out.println("\tillegal role (" + role + ")!");
+ return false;
+ }
+ }
+ else if (linkedElementName.equals(INPUTATTRIBUTES)) {
+ if (!Utils.equals(role, INPUT_ATTRIBUTES)) {
+ out.println("\tillegal role (" + role + ")!");
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true, if some string s1 equals some of the strings in array ss
+ private static boolean compareStrings(String s1, String[] ss) {
+ for (int i = 0; i< ss.length; i++) {
+ if (s1.equals(ss[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ */
+
+ private static String getFixBitmapPath(Document doc) {
+ Element root = doc.getDocumentElement();
+ return root.getAttribute(FIX_BITMAP_PATH);
+ }
+
+ private static String getStdBitmapPath(Document doc) {
+ Element root = doc.getDocumentElement();
+ return root.getAttribute(STD_BITMAP_PATH);
+ }
+
+ private static BufferedImage getImageFile(Element element, String attrib, String imagepath) throws IOException {
+ String s = element.getAttribute(attrib);
+ File f = null;
+ if (s.isEmpty()) {
+ return null;
+ }
+ try {
+ f = new File(imagepath + s);
+ return ImageIO.read(f);
+ }
+ catch (IOException ex) {
+ throw new IOException(ex.getMessage() + " " + f);
+ }
+ }
+
+ public static String writeToString(Node node) {
+ if (node == null) {
+ return "null";
+ }
+ LSSerializer dom3Writer = IMPL_LS.createLSSerializer();
+ DOMConfiguration config = dom3Writer.getDomConfig();
+ config.setParameter("format-pretty-print", Boolean.TRUE);
+ return dom3Writer.writeToString(node);
+ }
+
+ public static String writeToStringNoDec(Node node) {
+ if (node == null) {
+ return "null";
+ }
+ LSSerializer dom3Writer = IMPL_LS.createLSSerializer();
+ DOMConfiguration config = dom3Writer.getDomConfig();
+ config.setParameter("format-pretty-print", Boolean.TRUE);
+ config.setParameter("xml-declaration", Boolean.FALSE);
+ config.setParameter("discard-default-content", Boolean.FALSE);
+ return dom3Writer.writeToString(node);
+ }
+
+ /* OLD not recursion
+ public static Element parseFragment(String text, Element otherElement, boolean asSibling) {
+ LSParser parser =
+ IMPL_LS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS,
+ "http://www.w3.org/2001/XMLSchema");
+
+ LSInput input = IMPL_LS.createLSInput();
+ input.setStringData(text);
+ Element child = parser.parse(input).getDocumentElement();
+ Document doc = otherElement.getOwnerDocument();
+ Element adoptedChild = (Element) doc.adoptNode(child);
+
+ //find father element
+ Element father = otherElement;
+ if (asSibling) { //FIXME What if otherElement is not a ISOBUS-object
+ father = (Element) otherElement.getParentNode();
+ }
+
+ // father a real ISOBUS-object -> create a new link node and possibly a new actual node
+ if (equals(father.getNodeName(), OBJECTS)) { //yes
+ System.out.println("ISOBUS OBJECT");
+ // create new link-node with all link-attributes
+ Element link = doc.createElement("include_object");
+ //copyAttributes(adoptedChild, link, "name", "pos_x", "pos_y", "block_col", "block_row", "block_font");
+ Tools.copyAndCreateAttributes(adoptedChild, link, new String[] {
+ "name", "pos_x", "pos_y", "block_col", "block_row", "block_font"},
+ new String[] {"", "0", "0", "0", "0", ""});
+
+ // remove all link-attributes except name
+ removeAttributes(adoptedChild, "pos_x", "pos_y", "block_col", "block_row", "block_font");
+
+ // add node to root if it doesn't exist
+ Element root = (Element) doc.getFirstChild();
+ System.out.println("Root: " + root);
+ if (!elementExist(root, adoptedChild)) {
+ System.out.println("Element does not exist");
+ root.appendChild(adoptedChild);
+ }
+
+ // add link to given place
+ if (asSibling) {
+ father.insertBefore(link, otherElement);
+ }
+ else {
+ otherElement.appendChild(link);
+ }
+ }
+ // father is not a real ISOBUS-object -> do not bother with the link, just remove all context info
+ else {
+ System.out.println("NOT A ISOBUS OBJECT");
+ // remove all link-attributes except name
+ removeAttributes(adoptedChild, "pos_x", "pos_y", "block_col", "block_row", "block_font");
+ // add node to given place
+ if (asSibling) {
+ father.insertBefore(adoptedChild, otherElement);
+ }
+ else {
+ otherElement.appendChild(adoptedChild);
+ }
+ }
+ return adoptedChild;
+ }
+ */
+
+ /**
+ * Parses a XML fragment which can contain objects inside other objects
+ */
+ public static Element parseFragment(String text, Document doc) {
+ LSParser parser =
+ IMPL_LS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS,
+ "http://www.w3.org/2001/XMLSchema");
+
+ LSInput input = IMPL_LS.createLSInput();
+ input.setStringData(text);
+ Element child = parser.parse(input).getDocumentElement();
+ return (Element) doc.adoptNode(child);
+ }
+
+ public static boolean insertFragment(Element fragment, Element actual, Element link, boolean asSibling) {
+
+ // System.out.println("OTHER NODE: " + otherElement + ", ITS PARENT: " + otherElement.getParentNode());
+ String trgName;
+ if (asSibling) {
+ Node parent;
+ if (link != null) {
+ trgName = link.getParentNode().getNodeName();
+ }
+ else {
+ trgName = actual.getParentNode().getNodeName();
+ }
+ /*
+ else if (actual != null) {
+ trgName = actual.getParentNode().getNodeName();
+ }
+ else {
+ asSibling = false;
+ actual = fragment.getOwnerDocument().getDocumentElement();
+ trgName = actual.getNodeName();
+ }
+ */
+ }
+ else {
+ trgName = actual.getNodeName();
+ }
+
+ // if we have a working link, the name of the fragment is the name
+ // of the linked object, not "include object"
+ String frgName = fragment.getNodeName();
+ if (Utils.equals(frgName, INCLUDE_OBJECT)) {
+ if (Utils.equals(trgName, OBJECTS)) {
+ Map nameMap = Tools.createNameMap(fragment.getOwnerDocument());
+ String name = fragment.getAttribute(NAME);
+ if (!name.isEmpty()) {
+ Element elem = nameMap.get(name);
+ if (elem != null) {
+ frgName = elem.getNodeName();
+ }
+ }
+ }
+ else {
+ System.err.println("*** INSERTING A LINK TO THE ROOT IS NOT ALLOWED (see Tools.java) ***");
+ return false;
+ }
+ }
+
+ //System.out.println("insertFragment: trg: " + trgName + ", frg: " + frgName);
+
+ //removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+
+ // objects with x-y-object references
+ if (Utils.equals(trgName, WORKINGSET, DATAMASK, ALARMMASK,
+ CONTAINER, KEY, BUTTON, AUXILIARYFUNCTION, AUXILIARYINPUT) &&
+ Utils.equals(frgName, CONTAINER, BUTTON,
+ INPUTBOOLEAN, INPUTSTRING, INPUTNUMBER, INPUTLIST, OUTPUTSTRING,
+ OUTPUTNUMBER, LINE, RECTANGLE, ELLIPSE, POLYGON, METER,
+ LINEARBARGRAPH, ARCHEDBARGRAPH, PICTUREGRAPHIC, OBJECTPOINTER)) {
+
+ createMissingAttributes(fragment,
+ new String[] {POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT},
+ new String[] {"0", "0", "0", "0", ""});
+
+ removeAttributes(fragment, ROLE);
+ }
+ // objects with bare-object references
+ else if ((Utils.equals(trgName, INPUTLIST) &&
+ Utils.equals(frgName, CONTAINER, OUTPUTSTRING, OUTPUTNUMBER,
+ LINE, RECTANGLE, ELLIPSE, POLYGON, PICTUREGRAPHIC)) ||
+ (Utils.equals(trgName, SOFTKEYMASK) &&
+ Utils.equals(frgName, KEY, OBJECTPOINTER))) {
+
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+ }
+ // objects with active-mask-role references
+ else if (Utils.equals(trgName, WORKINGSET) &&
+ Utils.equals(frgName, DATAMASK, ALARMMASK)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {ACTIVE_MASK});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with softkeymask-role references
+ else if (Utils.equals(trgName, DATAMASK, ALARMMASK) &&
+ Utils.equals(frgName, SOFTKEYMASK)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {SOFT_KEY_MASK});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with foreground-colour-role references
+ else if (Utils.equals(trgName, INPUTBOOLEAN) &&
+ Utils.equals(frgName, FONTATTRIBUTES)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {FOREGROUND_COLOUR});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with font-attributes-role references
+ else if (Utils.equals(trgName, INPUTSTRING, INPUTNUMBER, OUTPUTSTRING, OUTPUTNUMBER) &&
+ Utils.equals(frgName, FONTATTRIBUTES)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {FONT_ATTRIBUTES});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with input-attributes-role references
+ else if (Utils.equals(trgName, INPUTSTRING) &&
+ Utils.equals(frgName, INPUTATTRIBUTES)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {INPUT_ATTRIBUTES});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with variable-reference-role references (numbers)
+ else if (Utils.equals(trgName, INPUTNUMBER, INPUTLIST, OUTPUTNUMBER, METER, LINEARBARGRAPH, ARCHEDBARGRAPH) &&
+ Utils.equals(frgName, NUMBERVARIABLE)) {
+
+ // could be also target value variable reference... (but the default is variable-reference,
+ // there are no object types that have target value variable reference but no variable
+ // reference)
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {VARIABLE_REFERENCE});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with variable-reference-role references (strings)
+ else if (Utils.equals(trgName, INPUTSTRING, OUTPUTSTRING) &&
+ Utils.equals(frgName, STRINGVARIABLE)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {VARIABLE_REFERENCE});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with line-attribute-role references
+ else if (Utils.equals(trgName, LINE, RECTANGLE, ELLIPSE, POLYGON) &&
+ Utils.equals(frgName, LINEATTRIBUTES)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {LINE_ATTRIBUTES});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with fill-attribute-role references
+ else if (Utils.equals(trgName, RECTANGLE, ELLIPSE, POLYGON) &&
+ Utils.equals(frgName, FILLATTRIBUTES)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {FILL_ATTRIBUTES});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with value-role references (FIXME: could be more specific, e.g. not allow
+ // pointers to point to working sets, data masks, etc.)
+ else if (Utils.equals(trgName, OBJECTPOINTER)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {VALUE});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // objects with fill-pattern-role references
+ else if (Utils.equals(trgName, FILLATTRIBUTES) &&
+ Utils.equals(frgName, PICTUREGRAPHIC)) {
+
+ createMissingAttributes(fragment, new String[] {ROLE}, new String[] {FILL_PATTERN});
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // polygon points (could allow block_font positioning, but who cares...)
+ else if (Utils.equals(trgName, POLYGON) &&
+ Utils.equals(frgName, POINT)) {
+
+ createMissingAttributes(fragment, new String[] {POS_X, POS_Y}, new String[] {"0", "0"});
+ removeAttributes(fragment, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);//, NAME);
+ }
+ /*
+ // for copying broken links(?!?)
+ else if (Tools.equals(frgName, INCLUDE_OBJECT)) {
+
+ // just in case!
+ createMissingAttributes(fragment,
+ new String[] {POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE},
+ new String[] {"0", "0", "0", "0", "", ""});
+ }
+ */
+ // FIXME: could be much more specific: an object supports only certain macros
+ else if (Utils.equals(frgName, MACRO)) {
+
+ // defaults to the first possible role
+ createMissingAttributes(fragment, new String[] {ROLE}, AttributeTable.findPossibleRoles(trgName, MACRO));
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // a macro is a list of commands
+ else if (Utils.equals(trgName, MACRO) &&
+ frgName.startsWith(COMMAND)) {
+
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // FIXME: could be much more specific: a command can have only certain targets
+ else if (trgName.startsWith(COMMAND)) {
+ createMissingAttributes(fragment, new String[] {ROLE}, AttributeTable.findPossibleRoles(trgName, frgName));
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+ }
+ // root level objects have to be real isobus objects
+ else if (Utils.equals(trgName, OBJECTPOOL) &&
+ Utils.equals(frgName, OBJECTS)) {
+ // && !Tools.equals(frgName, INCLUDE_OBJECT)) { // does not work as include_objects have already been replaced!
+
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+ }
+ // illegal insertion!
+ else {
+ System.err.println("*** INSERTING " + frgName + " TO " + trgName + " IS NOT ALLOWED (see Tools.java) ***");
+ return false;
+ }
+ /*
+ WORKINGSET, DATAMASK, ALARMMASK, CONTAINER,
+ SOFTKEYMASK, KEY, BUTTON,
+ INPUTBOOLEAN, INPUTSTRING,
+ INPUTNUMBER, INPUTLIST,
+ OUTPUTSTRING, OUTPUTNUMBER,
+ LINE, RECTANGLE, ELLIPSE, POLYGON,
+ METER, LINEARBARGRAPH, ARCHEDBARGRAPH, PICTUREGRAPHIC,
+ NUMBERVARIABLE, STRINGVARIABLE,
+ FONTATTRIBUTES, LINEATTRIBUTES, FILLATTRIBUTES, INPUTATTRIBUTES,
+ OBJECTPOINTER, MACRO, AUXILIARYFUNCTION, AUXILIARYINPUT
+ */
+
+ if (asSibling) {
+ if (link != null) {
+ link.getParentNode().insertBefore(fragment, link);
+ }
+ else {
+ actual.getParentNode().insertBefore(fragment, actual);
+ }
+ }
+ // insert as child
+ else {
+ actual.appendChild(fragment);
+ }
+ return true;
+ }
+
+ /**
+ * Inserts a fragment at the specified position in the document.
+ public static void insertFragment(Element fragment, Element otherElement, boolean asSibling) {
+
+ // find father element
+ Element father = otherElement;
+ if (asSibling) {
+ father = (Element) otherElement.getParentNode();
+ }
+
+ Document doc = otherElement.getOwnerDocument();
+ // father a real ISOBUS-object -> create a new link node and possibly a new actual node
+ if (equals(father.getNodeName(), OBJECTS)) {
+ System.out.println("ISOBUS OBJECT");
+ // create new link-node with all link-attributes
+ Element link = doc.createElement(INCLUDE_OBJECT);
+ //copyAttributes(adoptedChild, link, "name", "pos_x", "pos_y", "block_col", "block_row", "block_font");
+ Tools.copyAndCreateAttributes(fragment, link, new String[] {
+ NAME, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT},
+ new String[] {"", "0", "0", "0", "0", ""});
+
+ // remove all link-attributes except name
+ removeAttributes(fragment, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+
+ // add node to root if it doesn't exist
+ Element root = (Element) doc.getFirstChild();
+ System.out.println("Root: " + root);
+ if (!elementExist(root, fragment)) {
+ System.out.println("Element does not exist");
+ root.appendChild(fragment);
+ }
+
+ // add link to given place
+ if (asSibling) {
+ father.insertBefore(link, otherElement);
+ }
+ else {
+ otherElement.appendChild(link);
+ }
+ }
+ // father is not a real ISOBUS-object -> do not bother with the link, just remove all context info
+ else {
+ System.out.println("NOT A ISOBUS OBJECT");
+ // remove all link-attributes except name
+ removeAttributes(fragment, "pos_x", "pos_y", "block_col", "block_row", "block_font");
+ // add node to given place
+ if (asSibling) {
+ father.insertBefore(fragment, otherElement);
+ }
+ else {
+ otherElement.appendChild(fragment);
+ }
+ }
+
+ recursiveRemoveNesting(fragment, doc.getFirstChild());
+ }
+ */
+
+ /**
+ * Check for loops in the object pool.
+ * @param fragment
+ * @param actual
+ * @param link
+ * @param asSibling
+ * @return
+ * @throws pooledit.PoolException
+ */
+ public static boolean createsLoop(Element fragment, Element actual, Element link, boolean asSibling) throws PoolException {
+ LinkedList worklist = new LinkedList();
+
+ if (!fragment.getNodeName().equals(INCLUDE_OBJECT)) {
+ throw new PoolException(fragment.getNodeName() + " is not include_object");
+ }
+
+ Map nameMap = createNameMap(actual.getOwnerDocument());
+ String name = fragment.getAttribute(NAME);
+ Element target = nameMap.get(name);
+ if (target == null) {
+ throw new PoolException("\"" + name + "\" is not a valid name");
+ }
+
+ // Finding the parent node is quite complicated: is the include_object
+ // fragment is being inserted as a sibling to a link node, then the
+ // common parent is the parent of the link (not the actual object).
+ Node parent;
+ if (asSibling) {
+ parent = (link != null) ?
+ link.getParentNode() :
+ actual.getParentNode();
+ }
+ else {
+ parent = actual;
+ }
+
+ if (parent.getNodeType() == Element.ELEMENT_NODE &&
+ Utils.equals(parent.getNodeName(), OBJECTS)) {
+
+ worklist.addLast((Element) parent);
+ }
+
+ while (!worklist.isEmpty()) {
+ Element element = worklist.removeFirst();
+
+ //System.out.println("CHECKING: " + element.getAttribute(NAME));
+ // one of the parents is the target element -> loop
+ if (element == target) {
+ return true;
+ }
+
+ findParentElements(element, worklist);
+ }
+ //System.out.println("NO LOOPS FOUND!");
+ return false;
+ }
+
+ /**
+ * Finds all parents of the specified element and adds them to the given
+ * list. An object can have one immediate parent (the enclosing element)
+ * and multiple non-immediate parents (elements that link to this object,
+ * i.e. include objects).
+ * @param elem
+ * @param list
+ */
+ public static void findParentElements(Element elem, List list) {
+
+ // look for real parent (the enclosing element)
+ Node parent = elem.getParentNode();
+ if (parent.getNodeType() == Element.ELEMENT_NODE &&
+ Utils.equals(parent.getNodeName(), OBJECTS)) {
+
+ list.add((Element) parent);
+ }
+
+ // look for objects that link to this element
+ String name = elem.getAttribute(NAME);
+ if (name.isEmpty()) {
+ return;
+ }
+
+ // iterate over all elements in the document
+ Document doc = elem.getOwnerDocument();
+ NodeList nodes = doc.getElementsByTagName(INCLUDE_OBJECT);
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Element element = (Element) nodes.item(i);
+ // other objects can use include objects to make references
+ if (element.getAttribute(NAME).equals(name)) {
+ parent = element.getParentNode();
+ if (parent.getNodeType() == Element.ELEMENT_NODE) {
+
+ list.add((Element) parent);
+ }
+ }
+ }
+ }
+
+ public static void findAncestorElements(Element element, List ancestors) {
+ List worklist = new ArrayList();
+ worklist.add(element);
+ findAncestorElements(worklist, ancestors);
+ }
+ public static void findAncestorElements(List elements, List ancestors) {
+ List worklist = new ArrayList();
+ for (Element e : elements) {
+ findParentElements(e, worklist);
+ ancestors.addAll(worklist);
+ findAncestorElements(worklist, ancestors);
+ }
+ }
+
+ /**
+ * Copies the specified attributes from the source element to the target
+ * element.
+ */
+ static public void copyAttributes(Element src, Element trg, String ... attributes) {
+ for (int i = 0; i < attributes.length; i++) {
+ if (src.hasAttribute(attributes[i])) {
+ trg.setAttribute(attributes[i], src.getAttribute(attributes[i]));
+ }
+ }
+ }
+
+ /**
+ * Checks whether the target element can be replaced by a link to the
+ * source element. The source has to have every attribute the target has
+ * except POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT and ROLE. The
+ * values of the arguments must also match.
+ */
+ static public boolean equalAttributes(Element trg, Element src) {
+ NamedNodeMap attribs = trg.getAttributes();
+ int nro = attribs.getLength();
+
+ for (int i = 0; i < nro; i++) {
+ Node attr = attribs.item(i);
+ String name = attr.getNodeName();
+ if (Utils.equals(name, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE)) {
+ continue;
+ }
+ String trgval = attr.getNodeValue();
+ String srcval = src.getAttribute(name);
+ if (!trgval.equals(srcval)) {
+ System.err.println("attribute \"" + name + "\" has different value: " + trgval + " <-> " + srcval);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks all children of the specified element. If at least one child has
+ * the same name-attribute than adoptedChild, returns true.
+ private static boolean elementExist(Element element, Element adoptedChild) {
+ String name = adoptedChild.getAttribute(NAME);
+ NodeList childs = element.getChildNodes();
+ for (int i = 0, n = childs.getLength(); i < n; i++) {
+ Node node = childs.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ if (elem.getAttribute(NAME).equals(name)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ */
+
+ static private class Pair {
+ private A parent;
+ private B child;
+ Pair(A parent, B child) {
+ this.parent = parent;
+ this.child = child;
+ }
+ A getParent() { return (A) parent; }
+ B getChild() { return (B) child; }
+ }
+
+ /**
+ * XML2 -> XML1
+ *
+ * Substitutes all include objects with role attribute with appropriate
+ * attributes in their parent elements. E.g.
+ *
+ *
+ *
+ *
+ *
+ * translates to
+ *
+ *
+ * ...
+ *
+ */
+ public static void removeRoles(Element root) {
+ // while iterating through the document, all the insertions of new
+ // elements are stored in a work list (inserting new elements while
+ // iterating through the old ones, might yield unpredictable results)
+ List> worklist = new ArrayList>();
+
+ // iterate over all include objects
+ NodeList elements = root.getElementsByTagName(INCLUDE_OBJECT);
+ for (int i = 0; i < elements.getLength(); i++) {
+ Element element = (Element) elements.item(i);
+ String role = element.getAttribute(ROLE);
+ if (!role.isEmpty()) { //check that there is a role-attribute
+ Element parent = (Element) element.getParentNode();
+ String roleName = element.getAttribute(NAME);
+
+ // add an attribute to the parent, e.g. font_attributes="font6x8"
+ parent.setAttribute(role, roleName);
+
+ // schedule the element for deletion
+ worklist.add(new Pair(parent, element));
+ }
+ }
+ for (Pair p : worklist) {
+ Element child = p.getChild();
+ Element parent = p.getParent();
+
+ // include objects are removed
+ if (child.getNodeName().equals(INCLUDE_OBJECT)) {
+ parent.removeChild(child);
+ }
+ // other objects are moved to the root
+ else {
+ child.removeAttribute(ROLE);
+ root.appendChild(child);
+ }
+ }
+ }
+
+ /**
+ * Divides all angle attributes by two (effectively changes the unit
+ * from 1 degree to 2 degrees). NOTE: it is very debatable whether any
+ * XML format should be using 2 degrees as angle unit.
+ */
+ private static void divAngles(Element root) {
+ String angleAttrs[] = {START_ANGLE, END_ANGLE};
+
+ // iterate over all objects
+ NodeList elements = root.getElementsByTagName("*");
+ for (int i = 0; i < elements.getLength(); i++) {
+ Element element = (Element) elements.item(i);
+
+ // iterate over the specified attributes
+ for (String attribute: angleAttrs) {
+ if (!element.getAttribute(attribute).isEmpty()) {
+ int value = Integer.parseInt(element.getAttribute(attribute));
+ element.setAttribute(attribute, Integer.toString(value / 2));
+ }
+ }
+ }
+ }
+
+ /**
+ * XML1 -> XML2
+ *
+ * Substitutes all attributes that are actually object references with
+ * include objects. This makes the resulting document easier to work with
+ * as all references are include objects. Unlike attributes, the include
+ * objects can also be replaced by actual objects.
+ */
+ public static void createRoles(Element root) {
+
+ // while iterating through the document, all the insertions of new
+ // elements are stored in a work list (inserting new elements while
+ // iterating through the old ones, might yield unpredictable results)
+ List> worklist = new ArrayList>();
+
+ // iterate over all elements and build work list
+ NodeList elements = root.getElementsByTagName("*");
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ createRole((Element) elements.item(i), worklist);
+ }
+
+ // work through the list
+ for (Pair p : worklist) {
+ p.getParent().insertBefore(p.getChild(), p.getParent().getFirstChild());
+ }
+ }
+
+ static void createRole(Element element, List> worklist) {
+ // attributes that are checked
+ final String attributes[] = {
+ ACTIVE_MASK, SOFT_KEY_MASK, FONT_ATTRIBUTES, FOREGROUND_COLOUR,
+ INPUT_ATTRIBUTES, VARIABLE_REFERENCE, TARGET_VALUE_VARIABLE_REFERENCE,
+ LINE_ATTRIBUTES, FILL_ATTRIBUTES, FILL_PATTERN
+ };
+
+ // iterate over all attributes
+ Document doc = element.getOwnerDocument();
+ for (int j = 0, m = attributes.length; j < m; j++) {
+ String role = attributes[j];
+ String roleName = element.getAttribute(role);
+
+ // the attribute MUST BE REMOVED, or else the following
+ // question must be answered:
+ // - what happens if the attribute is changed and the current
+ // role object is embedded object (not just a link)?
+ element.removeAttribute(role);
+
+ if (!roleName.isEmpty()) {
+ // creating a new role attribute
+ Element includeObject = doc.createElement(INCLUDE_OBJECT);
+ includeObject.setAttribute(ROLE, role);
+ includeObject.setAttribute(NAME, roleName);
+ worklist.add(new Pair(element, includeObject));
+ }
+ }
+
+ // object pointer require special treatment
+ if (element.getTagName().equals(OBJECTPOINTER) &&
+ !element.getAttribute(VALUE).isEmpty()) { //element.hasAttribute(VALUE)) {
+
+ String roleName = element.getAttribute(VALUE);
+ element.removeAttribute(VALUE);
+ Element includeObject = doc.createElement(INCLUDE_OBJECT);
+ includeObject.setAttribute(ROLE, VALUE);
+ includeObject.setAttribute(NAME, roleName);
+ worklist.add(new Pair(element, includeObject));
+ }
+ }
+
+ /**
+ * Removes objects that are inside other objects (embedded elements)
+ public static void removeNesting(Document doc) {
+ Tools.recursiveRemoveNesting(doc.getDocumentElement(), null);
+ }
+
+ private static void recursiveRemoveNesting(Element element,
+ Node objectContainer) {
+
+ if (element.getNodeName().equals(OBJECTPOOL)) {
+ objectContainer = element;
+ }
+
+ Element child = Tools.getFirstChildElement(element);
+ while (child != null) {
+ Element nextElement = Tools.getNextSiblingElement(child);
+
+ // valid tag name?
+ if (equals(child.getNodeName(), OBJECTS)) {
+ Tools.recursiveRemoveNesting(child, objectContainer);
+ if (element != objectContainer) {
+
+ // create include object element with proper
+ // attributes set
+ Element link = element.getOwnerDocument().createElement(INCLUDE_OBJECT);
+
+ // this should be done automatically by createElement, but it is not?!?
+ Tools.copyAndCreateAttributes(child, link, new String[] {
+ NAME, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT},
+ new String[] {"", "0", "0", "0", "0", ""});
+
+ // replace the original element with the link
+ // element
+ element.replaceChild(link, child);
+
+ // remove now obsolete attributes
+ Tools.removeAttributes(child, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT);
+
+ // place the original at the end of the object
+ // container
+ objectContainer.appendChild(child);
+ }
+ }
+ child = nextElement;
+ }
+ }
+ */
+
+ /**
+ * Gets the first child element.
+ */
+ private static Element getFirstChildElement(Element parent) {
+ if (parent == null) {
+ return null;
+ }
+ Node child = parent.getFirstChild();
+ while (child != null && child.getNodeType() != Node.ELEMENT_NODE) {
+ child = child.getNextSibling();
+ }
+ return (Element) child;
+ }
+
+ /**
+ * Gets the next sibling element, or null if there are none.
+ */
+ public static Element getNextSiblingElement(Element element) {
+ if (element == null) {
+ return null;
+ }
+ Node sibling = element.getNextSibling();
+ while (sibling != null && sibling.getNodeType() != Node.ELEMENT_NODE) {
+ sibling = sibling.getNextSibling();
+ }
+ return (Element) sibling;
+ }
+
+ /**
+ * Gets the previous sibling element, or null if there are none.
+ */
+ public static Element getPrevSiblingElement(Element element) {
+ if (element == null) {
+ return null;
+ }
+ Node sibling = element.getPreviousSibling();
+ while (sibling != null && sibling.getNodeType() != Node.ELEMENT_NODE) {
+ sibling = sibling.getPreviousSibling();
+ }
+ return (Element) sibling;
+ }
+
+ /**
+ * Gets a list of child elements for the specified parent element.
+ * Node.getChildNodes() method returns all kinds of nodes. This method
+ * filters out only element nodes and returns them as a list.
+ */
+ public static List getChildElementList(Element parent) {
+ NodeList children = parent.getChildNodes();
+ int n = children.getLength();
+ List elements = new ArrayList(n); // this list cannot have more than n elements
+ for (int i = 0; i < n; i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ elements.add((Element) child);
+ }
+ }
+ return elements;
+ }
+
+ /**
+ * Removes the specified attributes.
+ */
+ public static void removeAttributes(Element node, String ... names) {
+ for (int i = 0, n = names.length; i < n; i++) {
+ node.removeAttribute(names[i]);
+ }
+ }
+
+ /**
+ * This method combines recursively element and it's linked or actual
+ * children in to a single composite object.
+ */
+ public static Element createMergedElementRecursive(Element element, Map nameMap) {
+ Document doc = element.getOwnerDocument();
+ Element actual;
+ Element link;
+
+ // element is link object
+ if (Utils.equals(element.getNodeName(), INCLUDE_OBJECT)) {
+ // check for broken links
+ actual = nameMap.get(element.getAttribute(NAME));
+ link = element;
+ }
+ // element is actual object
+ else {
+ actual = element;
+ link = null;
+ }
+
+ // create merged element and copy all attributes from link and actual node to it
+ Element newelement = null;
+ if (actual != null) {
+ newelement = doc.createElement(actual.getNodeName());
+ copyAllMissingAttributes(actual, newelement);
+ if (link != null) {
+ copyAllMissingAttributes(link, newelement);
+ }
+ // add included objects with recursion to merged element
+ NodeList children = actual.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element newchild = createMergedElementRecursive((Element) node, nameMap);
+ if (newchild != null) {
+ newelement.appendChild(newchild);
+ }
+ }
+ }
+ }
+ // broken link
+ else if (link != null) {
+ newelement = doc.createElement(link.getNodeName());
+ copyAllMissingAttributes(link, newelement);
+ }
+
+ return newelement;
+ }
+
+ private static void copyAllMissingAttributes(Element from, Element to) {
+ NamedNodeMap attrs = from.getAttributes();
+ for (int i = 0, n = attrs.getLength(); i < n; i++) {
+ Attr attr = (Attr) attrs.item(i);
+ String name = attr.getName();
+ String value = attr.getValue();
+ if (!to.hasAttribute(name)) {
+ to.setAttribute(name, value);
+ }
+ }
+ }
+
+ /**
+ * Copies attributes from the source element to the target element.
+ * If the source does not have the specified attribute, the specified
+ * default value is used. The existing attributes in the target element
+ * are overriden.
+ */
+ public static void copyAndCreateAttributes(Element src, Element trg,
+ String[] names, String[] defaults) {
+ for (int i = 0, n = names.length; i < n; i++) {
+ String name = names[i];
+ if (src.hasAttribute(name)) {
+ trg.setAttribute(name, src.getAttribute(name));
+ }
+ else {
+ trg.setAttribute(name, defaults[i]);
+ }
+ }
+ }
+
+ /**
+ * Creates the specified attributes with the specified default values.
+ * The existing attributes are never overriden.
+ */
+ private static void createMissingAttributes(Element to, String[] names,
+ String[] defaults) {
+
+ for (int i = 0, n = names.length; i < n; i++) {
+ String name = names[i];
+ if (!to.hasAttribute(name)) {
+ to.setAttribute(name, defaults[i]);
+ }
+ }
+ }
+
+ /**
+ * Tries to find the given element from a string
+ * If it doesn't exist returns -1
+ *
+ * FIXME: this works only if names are unambiguous. Elements should be
+ * searched by their paths. That would require the search function to
+ * understand the structure of the xml file.
+ */
+ public static int findElementFromString(String text, String elementType, String elementName) {
+ int index = 0;
+ while ((index = text.indexOf(elementType, index + 1)) != -1) {
+ if (text.indexOf(elementName, index + 1) < text.indexOf(">", index + 1)) {
+ return index - 1;
+ }
+ }
+ return -1;
+ }
+
+ public static int findElementEnd(String text, String elementType, int index) {
+ int oldIndex = index;
+ return ((index = text.indexOf(">", index)) != -1) ? index + 1 : oldIndex;
+ }
+
+ /**
+ * Searches for attribute-object that have an equalent object in the documents root
+ * and replaces it with a link (include_object)
+ * Search begins from given element.
+ * FIXME: can be removed?
+ public static void optimize(Element parent) {
+ String [] attributes = {FONTATTRIBUTES, LINEATTRIBUTES, FILLATTRIBUTES};
+ Document doc = parent.getOwnerDocument();
+
+ NodeList docElements = doc.getDocumentElement().getChildNodes( );
+
+ //itarate all types of attribute-elements
+ for(int i = 0; i < attributes.length; i++) {
+ NodeList attributeElements = parent.getElementsByTagName( attributes[i] );
+
+ //iterate all child elements of the parent
+ for(int j = 0; j < attributeElements.getLength(); j++)
+ if( attributeElements.item(j).getNodeType() == Node.ELEMENT_NODE ) {
+ Element element1 = (Element) attributeElements.item(j);
+ boolean equalIsFound = false;
+
+ //iterate all elements in the root of the document
+ for(int k = 0; k < docElements.getLength() && !equalIsFound; k++)
+ if(( docElements.item(k).getNodeType() == Node.ELEMENT_NODE ) &&
+ ( elementsAreEqual( element1, ( Element) docElements.item(k) ) )) {
+
+ Element element2 = ( Element) docElements.item(k);
+
+ //An equal attribute element is found, replace it with a link
+ equalIsFound = true;
+ String name = element1.getAttribute("name");
+ Element link = doc.createElement( "include_object" );
+ link.setAttribute("name", name);
+ Element realParent = (Element) element1.getParentNode();
+
+ System.out.println("Found Equal attributes (parent: "+ realParent + ") : " + element1 + ", " + element2);
+ realParent.appendChild( link );
+ realParent.removeChild( element1 );
+ //realParent.replaceChild( element1, link);
+ }
+ }
+ }
+ }
+ */
+
+ /**
+ * Checks if two attribute-elments are equal. etc they are same type and have all the same attributes.
+ * FIXME: can be removed?
+ private static boolean elementsAreEqual( Element element1, Element element2) {
+ //First check node name
+ if( ! element1.getNodeName().equals( element2.getNodeName()) )
+ return false;
+
+ //compare all wanted attributes
+ String attributes[] = {"name", //For All
+ "font_colour", "font_size", "font_style", "font_type", //font attributes
+ "line_art", "line_colour", "line_width", //line attributes
+ "fill_colour", "fill_pattern", "fill_type"}; //fill attributes
+
+ for(int i = 0; i < attributes.length; i++)
+ if( ! element1.getAttribute( attributes[i] ).equals(element2.getAttribute( attributes[i] ) ))
+ return false;
+
+ return true;
+ }
+ */
+}
diff --git a/src/pooledit/TreeEditPopup.java b/src/pooledit/TreeEditPopup.java
new file mode 100644
index 0000000..daf0bcc
--- /dev/null
+++ b/src/pooledit/TreeEditPopup.java
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package pooledit;
+
+import color.ColorPalette;
+import static pooledit.Definitions.*;
+import java.awt.Dimension;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.swing.JOptionPane;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.util.HashSet;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.tree.TreePath;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import treemodel.XMLTreeModel;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public abstract class TreeEditPopup {
+
+ static private final int
+ FLIP_VERTICAL = 0,
+ FLIP_HORIZONTAL = 1,
+ ROTATE_90_CW = 2,
+ ROTATE_90_CCW = 3,
+ ROTATE_180 = 4;
+
+ private final JPopupMenu popup;
+
+ public abstract TreePath getCurrentPath();
+
+ public abstract XMLTreeModel getXMLTreeModel();
+
+ public TreeEditPopup() {
+ popup = createPopupMenu();
+ }
+
+ private JPopupMenu createPopupMenu() {
+ JPopupMenu pup = new JPopupMenu();
+
+ JMenuItem uniqueItem = new JMenuItem("Make Unique (Deep)");
+ uniqueItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode node = (XMLTreeNode) getCurrentPath().getLastPathComponent();
+ if (node.isType(OBJECTS)) {
+ Element actual = node.actual();
+ Element link = node.link();
+ Map nameMap = node.getModel().getNameMap();
+ Element merged = Tools.createMergedElementRecursive(actual, nameMap);
+
+ // setup unique name
+ String name = merged.getAttribute(NAME);
+ String newname = Tools.findFreeName(name, nameMap);
+ merged.setAttribute(NAME, newname);
+
+ // if node is a link node, then the link is replaced, otherwise the
+ // actual object is replaced
+ Element target = (link != null) ? link : actual;
+ Tools.copyAttributes(target, merged, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+ target.getParentNode().replaceChild(merged, target);
+ }
+ }
+ });
+ /*
+ //FIXME this works only for links
+ Document doc = node.getModel().getDocument();
+ Element actual = node.actual();
+ Element link = node.link();
+ if (link != null) {
+ Element root = doc.getDocumentElement();
+ String name = actual.getAttribute(NAME);
+ // count all include_object-elements that link to element
+ NodeList nodes = doc.getElementsByTagName(INCLUDE_OBJECT);
+ int links = 0;
+ for (int i = 0; i < nodes.getLength(); i++) {
+ if (((Element) nodes.item(i)).getAttribute(NAME).equals(name)) {
+ links++;
+ }
+ }
+ System.out.println(getClass().getName() + " Make Unique: links: " + links);
+ if (links <= 1) {
+ return; // already unique!
+ }
+ //clone the element, rename it and add to root FIXME attributes won't copy
+ Element clone = (Element) doc.importNode(actual, true);
+ root.appendChild(clone);
+ String newname = Tools.findFreeName(name, getXMLTreeModel().getNameMap());
+ clone.setAttribute(NAME, newname);
+ link.setAttribute(NAME, newname);
+ }
+ // not a link
+ else {
+ //Check if there is a object with same name (not include_object) FIXME not implemented yet
+ // FIXME: how does this work? is this the same as "make duplicate",
+ // but only makes a duplicate, if there are referenes to this object?
+ if (!actual.hasAttribute(NAME)) {
+ JOptionPane.showMessageDialog(null,
+ "The selected object does not have a name!",
+ "Make Unique Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ String name = Tools.findFreeName( actual.getAttribute("name"), getXMLTreeModel().getNameMap());
+ actual.setAttribute("name", name);
+ }
+ }
+ });
+ */
+ pup.add(uniqueItem);
+
+ JMenuItem duplicateItem = new JMenuItem("Make Duplicate (Deep)");
+ duplicateItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode node = (XMLTreeNode) getCurrentPath().getLastPathComponent();
+ Element actual = node.actual();
+ Element link = node.link();
+
+ Element target = (link != null) ? link : actual;
+ //clone the link and add to father FIXME attributes won't copy (?)
+ Element duplicate = (Element) target.cloneNode(true);
+ //Tools.copyAllMissingAttributes(link, duplicate);
+
+ // links can have identical names, but actual object should
+ // prefer to have (globally) unique names
+ if (link == null && node.isType(OBJECTS)) {
+ // setup unique name
+ String name = duplicate.getAttribute(NAME);
+ String newname = Tools.findFreeName(name, Tools.createNameMap(node.getModel().getDocument(), true));
+ // node.getModel().getNameMap());
+ duplicate.setAttribute(NAME, newname);
+ }
+ target.getParentNode().insertBefore(duplicate, target.getNextSibling());
+ }
+ });
+ pup.add(duplicateItem);
+
+ JMenuItem linkableItem = new JMenuItem("Make Linkable");
+ linkableItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode node = (XMLTreeNode) getCurrentPath().getLastPathComponent();
+
+ if (node.link() != null) {
+ JOptionPane.showMessageDialog(null,
+ "Links cannot be made linkable!",
+ "Make Linkable Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (!node.isType(OBJECTS)) {
+ JOptionPane.showMessageDialog(null,
+ "The selected object (" + node.getType() +
+ ") cannot be made linkable!",
+ "Make Linkable Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ Element actual = node.actual();
+ XMLTreeModel model = node.getModel();
+ Map nameMap = model.getNameMap();
+ Document doc = model.getDocument();
+ Element root = doc.getDocumentElement();
+ Node parent = actual.getParentNode();
+
+ if (root == parent) {
+ JOptionPane.showMessageDialog(null,
+ "The selected object is already linkable!",
+ "Make Linkable Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // create link
+ Element link = doc.createElement(INCLUDE_OBJECT);
+
+ // setup unique names
+ String name = actual.getAttribute(NAME);
+ if (name.isEmpty()) {
+ name = actual.getNodeName();
+ }
+ if (nameMap.containsKey(name)) {
+ String newname = Tools.findFreeName(name, nameMap);
+ actual.setAttribute(NAME, newname);
+ }
+ Tools.copyAttributes(actual, link, NAME, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+
+ // replace actual node with the link and put the actual
+ // object in root
+ Node mark = actual.getNextSibling();
+ root.appendChild(actual);
+ parent.insertBefore(link, mark);
+
+ // remove extra attributes
+ Tools.removeAttributes(actual, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+ }
+ });
+ pup.add(linkableItem);
+
+ pup.addSeparator();
+
+ JMenuItem normalizeItem = new JMenuItem("Normalize Object");
+ normalizeItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode node = (XMLTreeNode) getCurrentPath().getLastPathComponent();
+
+ // normalize width and height input and output strings and
+ // numbers
+ if (node.isType(OUTPUTSTRING, INPUTSTRING, OUTPUTNUMBER, INPUTNUMBER)) {
+ String value;
+ if (node.isType(OUTPUTSTRING, INPUTSTRING)) {
+ value = node.getValue();
+ } else {
+ value = node.getFormatedNumber();
+ }
+ int length = value.length();
+ // we do not really care about the color palette (color reduction etc)
+ Dimension fontdim = node.getFontAttributes().getFont(ColorPalette.COLOR_8BIT).getDimension();
+ int width = fontdim.width * length;
+ int height = fontdim.height;
+ Element element = node.actual();
+ //element.setAttribute(LENGTH, Integer.toString(length)); // not for numbers!
+
+ // IDEA: depending on the horizontal justification, we could use delta width to
+ // properly align the object?
+
+ element.setAttribute(WIDTH, Integer.toString(width));
+ element.setAttribute(HEIGHT, Integer.toString(height));
+ } // normalize width and height of an input list to match the
+ // maximum dimensions of its children
+ else if (node.isType(INPUTLIST)) {
+ int maxw = 0;
+ int maxh = 0;
+ Dimension size = new Dimension();
+ XMLTreeModel model = node.getModel();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ nd.getNodeSize(size);
+ maxw = Math.max(maxw, size.width);
+ maxh = Math.max(maxh, size.height);
+ }
+ Element element = node.actual();
+ element.setAttribute(WIDTH, Integer.toString(maxw));
+ element.setAttribute(HEIGHT, Integer.toString(maxh));
+ } // normalize width and height of container, button and polygon objects
+ else if (node.isType(CONTAINER, BUTTON, POLYGON)) {
+ int minx = 0x7FFF; // FIXME: magic number
+ int miny = 0x7FFF;
+ int maxx = 0;
+ int maxy = 0;
+ Dimension size = new Dimension();
+ XMLTreeModel model = node.getModel();
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ nd.getNodeSize(size);
+ Integer x = nd.getX();
+ if (x != null) {
+ minx = Math.min(minx, x);
+ maxx = Math.max(maxx, x + size.width);
+ }
+ Integer y = nd.getY();
+ if (y != null) {
+ miny = Math.min(miny, y);
+ maxy = Math.max(maxy, y + size.height);
+ }
+ }
+ // leave room for button borders
+ if (node.isType(BUTTON)) {
+ maxx += 8;
+ maxy += 8;
+ }
+
+ // abjust position
+ Element pos = node.link() != null ? node.link() : node.actual();
+ if (pos != null) {
+ changeAttribute(pos, "pos_x", minx);
+ changeAttribute(pos, "pos_y", miny);
+ }
+
+ // adjust object
+ Element element = node.actual();
+ element.setAttribute(WIDTH, Integer.toString(maxx - minx));
+ element.setAttribute(HEIGHT, Integer.toString(maxy - miny));
+
+ // adjust children
+ for (int i = 0, n = model.getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+ Element child = nd.link() != null ? nd.link() : nd.actual();
+ if (minx > 0 && child.hasAttribute(POS_X)) {
+ child.setAttribute(POS_X, Integer.toString(Integer.parseInt(child.getAttribute(POS_X)) - minx));
+ }
+ if (miny > 0 && child.hasAttribute(POS_Y)) {
+ child.setAttribute(POS_Y, Integer.toString(Integer.parseInt(child.getAttribute(POS_Y)) - miny));
+ }
+ }
+ } else if (node.isType(PICTUREGRAPHIC)) {
+ try {
+ BufferedImage image = node.getImageFile(); // for size calculation only
+ int picW = node.getWidth();
+ int imageW = image.getWidth();
+ if (picW != image.getWidth()) {
+ Element actual = node.actual();
+ actual.setAttribute(WIDTH, Integer.toString(imageW));
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ });
+ pup.add(normalizeItem);
+
+ JMenuItem renameItem = new JMenuItem("Rename Object");
+ renameItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode child = (XMLTreeNode) getCurrentPath().getLastPathComponent();
+ Element actual = child.actual();
+ if (!actual.hasAttribute(NAME)) {
+ JOptionPane.showMessageDialog(null,
+ "The selected object does not have a name!",
+ "Rename Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ String oldName = actual.getAttribute(NAME);
+ String newName = (String) JOptionPane.showInputDialog(null /*parent*/,
+ "Please give a new name", "Rename Object",
+ JOptionPane.QUESTION_MESSAGE,
+ null, null, oldName);
+
+ try {
+ renameObject(child, newName);
+ }
+ catch (IllegalStateException ex) {
+ JOptionPane.showMessageDialog(null,
+ "The specified name already exists!",
+ "Rename Error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ });
+ pup.add(renameItem);
+
+ JMenuItem deleteItem = new JMenuItem("Delete Object");
+ deleteItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ XMLTreeNode node = (XMLTreeNode) getCurrentPath().getLastPathComponent();
+ Element actual = node.actual();
+ Element link = node.link();
+
+ // actual object (or broken link)
+ if (link == null || node.isType(INCLUDE_OBJECT)) {
+ actual.getParentNode().removeChild(actual);
+ } // working link (link != null)
+ else {
+ link.getParentNode().removeChild(link);
+ }
+ }
+ });
+ pup.add(deleteItem);
+
+ JMenuItem optimizeItem = new JMenuItem("Optimize Object");
+ optimizeItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ XMLTreeNode node = (XMLTreeNode) getCurrentPath().getLastPathComponent();
+ Element actual = node.actual();
+ Element link = node.link();
+
+ //XMLTreeModel model = node.getModel();
+ //Element candidate = model.getElementByName(node.getName());
+
+ Map nameMap = Tools.createNameMap(actual.getOwnerDocument());
+ Element candidate = nameMap.get(actual.getAttribute(NAME));
+ optimize(actual, candidate, nameMap);
+
+ //Tools.optimize( actual );
+ }
+ });
+ pup.add(optimizeItem);
+
+ pup.addSeparator();
+
+ JMenuItem bringFrontItem = new JMenuItem("Bring to Front");
+ bringFrontItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ moveFrontBack((XMLTreeNode) getCurrentPath().getLastPathComponent(), true, false);
+ }
+ });
+ pup.add(bringFrontItem);
+
+ JMenuItem bringForwardItem = new JMenuItem("Bring Forward");
+ bringForwardItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ moveFrontBack((XMLTreeNode) getCurrentPath().getLastPathComponent(), true, true);
+ }
+ });
+ pup.add(bringForwardItem);
+
+ JMenuItem sendBackwardItem = new JMenuItem("Send Backward");
+ sendBackwardItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ moveFrontBack((XMLTreeNode) getCurrentPath().getLastPathComponent(), false, true);
+ }
+ });
+ pup.add(sendBackwardItem);
+
+ JMenuItem sendBackItem = new JMenuItem("Send to Back");
+ sendBackItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ moveFrontBack((XMLTreeNode) getCurrentPath().getLastPathComponent(), false, false);
+ }
+ });
+ pup.add(sendBackItem);
+
+ pup.addSeparator();
+
+ JMenuItem mirrorVerticalItem = new JMenuItem("Flip Vertical ( | )");
+ mirrorVerticalItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ geometricTransformation((XMLTreeNode) getCurrentPath().getLastPathComponent(), FLIP_VERTICAL);
+ }
+ });
+ pup.add(mirrorVerticalItem);
+
+ JMenuItem mirrorHorizontalItem = new JMenuItem("Flip Horizontal (-)");
+ mirrorHorizontalItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ geometricTransformation((XMLTreeNode) getCurrentPath().getLastPathComponent(), FLIP_HORIZONTAL);
+ }
+ });
+ pup.add(mirrorHorizontalItem);
+
+ JMenuItem rotateClockwiseItem = new JMenuItem("Rotate 90\u00B0 CW");
+ rotateClockwiseItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ geometricTransformation((XMLTreeNode) getCurrentPath().getLastPathComponent(), ROTATE_90_CW);
+ }
+ });
+ pup.add(rotateClockwiseItem);
+
+ JMenuItem rotateAntiClockwiseItem = new JMenuItem("Rotate 90\u00B0 CCW");
+ rotateAntiClockwiseItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ geometricTransformation((XMLTreeNode) getCurrentPath().getLastPathComponent(), ROTATE_90_CCW);
+ }
+ });
+ pup.add(rotateAntiClockwiseItem);
+
+ JMenuItem rotateMoreItem = new JMenuItem("Rotate 180\u00B0");
+ rotateMoreItem.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ geometricTransformation((XMLTreeNode) getCurrentPath().getLastPathComponent(), ROTATE_180);
+ }
+ });
+ pup.add(rotateMoreItem);
+
+ //pup.addSeparator();
+ /*
+ TreePath path = getCurrentPath();
+ if (path != null && path.getPathCount() > 1) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+ Element fatherElement = ((XMLTreeNode) path.getPathComponent(path.getPathCount()-2)).actual();
+ // go trough all childs and find the child
+ NodeList childElements = fatherElement.getChildNodes();
+ for(int i = childElements.getLength() - 1; i >= 0; i--) { // last first
+ Node child = childElements.item(i);
+ // assumes linearized document and unique link names
+ // which is not true!
+ if (child.getNodeName().equals("include_object") &&
+ ((Element)child).getAttribute("name").equals(node.getName())) {
+ fatherElement.appendChild(child);
+ }
+ }
+ }
+ }
+ });
+ pup.add(moveFrontItem);
+ JMenuItem moveBackItem = new JMenuItem("Move to Back");
+ moveBackItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ System.out.println("Moving object to back");
+ TreePath path = getCurrentPath();
+ if (path != null && path.getPathCount() > 1) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+ Element fatherElement = ((XMLTreeNode) path.getPathComponent(path.getPathCount()-2)).actual();
+ NodeList childElements = fatherElement.getChildNodes();
+ //go trough all childs and find the child
+ for(int i = 0; i < childElements.getLength(); i++) { // first first
+ Node child = childElements.item(i);
+ if(child.getNodeName().equals("include_object") && ((Element)child).getAttribute("name").equals(node.getName())) {
+ fatherElement.insertBefore(child, childElements.item(0)); //insert first
+ }
+ }
+ }
+ }
+ });
+ pup.add(moveBackItem);
+ pup.addSeparator();
+ */
+ /*
+ JMenuItem meterWizard = new JMenuItem("Start Meter Wizard");
+ meterWizard.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ // FIXME: it might be a good idea to have only one meter wizard
+ // in the program?
+ // FIXME: it might also be better to put it in the menu
+ // instead?
+ JFrame wizardFrame = new JFrame("Meter Wizard");
+ MeterWizard wizard = new MeterWizard();
+ TreePath path = getCurrentPath();
+ // father element should not be category node!
+ // Element fatherElement = ((XMLTreeNode) path.getPathComponent(path.getPathCount()-2)).actual();
+ Element fatherElement = ((XMLTreeNode) path.getPathComponent(0)).actual(); // root
+ MeterGenerator generator = new MeterGenerator(wizard, fatherElement.getDocument(), getXMLTreeModel());
+ wizardFrame.getContentPane().add(wizard);
+ wizardFrame.pack();
+ wizardFrame.setVisible(true);
+ }
+ });
+ pup.add(meterWizard);
+ */
+
+ return pup;
+ }
+
+ /**
+ * Renames the specified object.
+ * FIXME: maybe this should be in Tools.java?
+ * @param child
+ * @param newName
+ */
+ static public void renameObject(XMLTreeNode child, String newName) {
+
+ // user has not entered a name
+ if (newName == null) {
+ return;
+ }
+
+ Element actual = child.actual();
+ if (actual == null || !actual.hasAttribute(NAME)) {
+ throw new IllegalArgumentException("the specified node has no name");
+ }
+
+ // the new name is the same as the old name
+ String oldName = actual.getAttribute(NAME);
+ if (newName.equals(oldName)) {
+ return;
+ }
+
+ // the name already exists
+ // if (getXMLTreeModel().getNameMap().containsKey(newName)) {
+ if (child.getModel().getNameMap().containsKey(newName)) {
+ throw new IllegalStateException("the name already exists");
+ }
+
+ System.out.println("Renaming, old name: " + oldName + " new name: " + newName);
+ // System.err.println("RENAME NEEDS A LOT OF FIXING!");
+ // NOTE: block_font must also be changed even though it is
+ // not a "real" attribute
+ //String attributes[] = {NAME, VARIABLE_REFERENCE,
+ //FONT_ATTRIBUTES, LINE_ATTRIBUTES, FILL_ATTRIBUTES,
+ //BLOCK_FONT};
+
+ // FIXME: this code assumes that the names are unique, e.g.
+ // if there are two actual objects with the same name and
+ // the user wants to rename one of them, both names will change!
+
+ Document doc = child.getModel().getDocument();
+ Element link = child.link();
+
+ // working link
+ if (link != null) {
+
+ // rename possible actual object in root
+ // NOTE: this may temporarily break a lot of links...
+ //System.out.println("CHANGING ROOT LEVEL OBJECT NAMES");
+ NodeList nodes = doc.getDocumentElement().getChildNodes();
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Node node = nodes.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ if (elem.getAttribute(NAME).equals(oldName)) {
+ elem.setAttribute(NAME, newName);
+ }
+ }
+ }
+
+ // rename possible links elsewhere in the document
+ //System.out.println("CHANGING LINK NAMES");
+ NodeList elements = doc.getElementsByTagName("*");
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Element elem = (Element) elements.item(i);
+ if (elem.getNodeName().equals(INCLUDE_OBJECT)) {
+ if (elem.getAttribute(NAME).equals(oldName)) {
+ elem.setAttribute(NAME, newName);
+ }
+ }
+ }
+ } // actual object or broken link
+ else if (actual != null) {
+
+ // NOTE: this may temporarily break a lot of links...
+ actual.setAttribute(NAME, newName);
+
+ // if the object is at the root level, update all links to it
+ if (actual.getParentNode().equals(doc.getDocumentElement())) {
+ NodeList elements = doc.getElementsByTagName("*");
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Element elem = (Element) elements.item(i);
+ if (elem.getNodeName().equals(INCLUDE_OBJECT)) {
+ if (elem.getAttribute(NAME).equals(oldName)) {
+ elem.setAttribute(NAME, newName);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Adapted from mouse controller.
+ */
+ static public void changeAttribute(Element element, String attr, int diff) {
+ String val = element.getAttribute(attr);
+ if (!val.isEmpty()) {
+ int p = Integer.parseInt(val);
+ if (diff != 0) {
+ element.setAttribute(attr, Integer.toString(p + diff));
+ }
+ }
+ }
+
+ /**
+ * Moves the selected node either as the last element (front) or the
+ * first element (back).
+ */
+ static public void moveFrontBack(XMLTreeNode node, boolean front, boolean one) {
+ Element elem = node.link() != null ? node.link() : node.actual();
+
+ if (elem == null) {
+ JOptionPane.showMessageDialog(null,
+ "The selected object is not in the document!",
+ "Move Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ Node parentNode = elem.getParentNode();
+ if (parentNode == null || parentNode.getNodeType() != Node.ELEMENT_NODE) {
+ JOptionPane.showMessageDialog(null,
+ "The selected object's parent is not document element!",
+ "Move Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ // one step
+ if (one) {
+ if (front) {
+ Element next = Tools.getNextSiblingElement(elem);
+ Element next2 = Tools.getNextSiblingElement(next);
+ parentNode.insertBefore(elem, next2);
+ } else {
+ Element prev = Tools.getPrevSiblingElement(elem);
+ if (prev != null) {
+ parentNode.insertBefore(elem, prev);
+ }
+ }
+ } // all the way
+ else {
+ if (front) {
+ parentNode.appendChild(elem);
+ } else {
+ parentNode.insertBefore(elem, parentNode.getFirstChild());
+ }
+ }
+ }
+
+ /**
+ * NOTE: if node points to the same object as candidate, then this method
+ * does nothing and returns true.
+ */
+ public static boolean optimize(Element actual, Element candidate, Map nameMap) {
+
+ List alist = Tools.getChildElementList(actual);
+ int n = alist.size();
+
+ boolean isOptimized = true;
+ for (int i = 0; i < n; i++) {
+ Element nd = alist.get(i);
+ Element md = nameMap.get(nd.getAttribute(NAME));
+ if (!optimize(nd, md, nameMap)) {
+ isOptimized = false;
+ }
+ }
+
+ String aname = actual.getAttribute(NAME);
+ if (candidate == null) {
+ System.err.println("no candidate for: " + aname);
+ return false;
+ }
+
+ String cname = actual.getAttribute(NAME);
+ if (!Tools.equalAttributes(actual, candidate)) {
+ System.err.println("attributes do not match: " + aname + " <-> " + cname);
+ return false;
+ }
+
+
+ List clist = Tools.getChildElementList(candidate);
+ int m = clist.size();
+ if (n != m) {
+ System.err.println("nro of children does not match: " +
+ aname + " (" + n + ") <-> " + cname + " (" + m + ")");
+ return false;
+ }
+
+ if (!isOptimized) {
+ System.err.println("children could not be optimized: " + aname + " <-> " + cname);
+ return false;
+ }
+
+ if (actual == candidate) {
+ System.err.println("elements are already optimized!");
+ return true;
+ }
+
+ System.out.println("optimizing: " + aname + " <-> " + cname);
+
+ Document doc = actual.getOwnerDocument();
+
+ // create link and set its attributes
+ Element link = doc.createElement(INCLUDE_OBJECT);
+ Tools.copyAttributes(actual, link, NAME, POS_X, POS_Y, BLOCK_COL, BLOCK_ROW, BLOCK_FONT, ROLE);
+
+ // replace actual node with the link and put the actual
+ // object in root
+ Element parent = (Element) actual.getParentNode();
+ parent.insertBefore(link, actual);
+ parent.removeChild(actual);
+
+ return isOptimized;
+ }
+
+ /**
+ * Applies the specified geometric transformation to the given node (and
+ * its children).
+ * @param node the node to be transformed
+ * @param xform the transformation
+ */
+ public static void geometricTransformation(XMLTreeNode node, int xform) {
+ // create a set to prevent multiple transformations
+ Set visitedElements = new HashSet();
+ String type = node.getType();
+ int width;
+ int height;
+
+ // do a recursive transformation
+ if (Utils.equals(type, DATAMASK, ALARMMASK)) {
+ XMLTreeNode root = (XMLTreeNode) node.getModel().getRoot();
+ width = root.getDimension();
+ height = width;
+ }
+ else if (Utils.equals(type, KEY, AUXILIARYFUNCTION, AUXILIARYINPUT, WORKINGSET)) {
+ XMLTreeNode root = (XMLTreeNode) node.getModel().getRoot();
+ width = root.getSKWidth();
+ height = root.getSKHeight();
+ }
+ else if (node.getWidth() != null) {
+ width = node.getWidth();
+ height = (node.getHeight() != null) ? node.getHeight() : width;
+ }
+ else {
+ // the selected node cannot be transformed!
+ return;
+ }
+ geometricTransformationRecursive(node, xform, width / 2, height / 2, visitedElements);
+ }
+
+ /**
+ * This is the recursive part of the geometricTransformation method.
+ * @param node
+ * @param xform
+ * @param midX
+ * @param midY
+ * @param visitedElements
+ */
+ private static void geometricTransformationRecursive(XMLTreeNode node,
+ int xform, int midX, int midY, Set visitedElements) {
+
+ Element actual = node.actual();
+ if (actual == null) {
+ return;
+ }
+
+ // add this element to the visited elements set so that it will not
+ // be processed again later
+ visitedElements.add(actual);
+
+ //System.out.println("Doing transformation " + xform + " for " + node + ", mid: (" + midX + "," + midY + ")");
+ String type = node.getType();
+
+ // transform polygon points
+ if (type.equals(POLYGON)) {
+
+ int points[] = node.getPolygonPoints();
+ for (int i = 0, n = points.length / 2; i < n; i++) {
+
+ // polygon points should not be rotated around the center point
+ int midXNew = midX;
+ int midYNew = midY;
+ if (xform == ROTATE_90_CW) {
+ midXNew = midY;
+ }
+ if (xform == ROTATE_90_CCW) {
+ midYNew = midX;
+ }
+ node.setPolygonPoint(i, xformX(xform, midXNew, midYNew, points[2 * i], points[2 * i + 1]),
+ xformY(xform, midXNew, midYNew, points[2 * i], points[2 * i + 1]));
+ }
+ }
+
+ // adjust start and end angle attributes
+ if (Utils.equals(type, ELLIPSE, METER, ARCHEDBARGRAPH)) {
+ int startAngle = node.getStartAngle();
+ int endAngle = node.getEndAngle();
+ switch (xform) {
+ case FLIP_VERTICAL: {
+ int tmp = (startAngle < 180) ? 180 - startAngle : 540 - startAngle;
+ startAngle = (endAngle < 180) ? 180 - endAngle : 540 - endAngle;
+ endAngle = tmp;
+ break;
+ }
+ case FLIP_HORIZONTAL: {
+ int tmp = 360 - startAngle;
+ startAngle = 360 - endAngle;
+ endAngle = tmp;
+ break;
+ }
+ case ROTATE_90_CW: {
+ startAngle -= 90;
+ endAngle -= 90;
+ break;
+ }
+ case ROTATE_90_CCW: {
+ startAngle += 90;
+ endAngle += 90;
+ break;
+ }
+ case ROTATE_180: {
+ startAngle += 180;
+ endAngle += 180;
+ break;
+ }
+ default:
+ throw new RuntimeException("no such transform (" + xform + ")");
+ }
+ if (startAngle < 0) {
+ startAngle += 360;
+ }
+ if (startAngle > 360) {
+ startAngle -= 360;
+ }
+ if (endAngle < 0) {
+ endAngle += 360;
+ }
+ if (endAngle > 360) {
+ endAngle -= 360;
+ }
+ node.setStartAngle(startAngle);
+ node.setEndAngle(endAngle);
+ }
+
+ // adjust meter attributes
+ if (type.equals(METER)) {
+ if (xform == FLIP_VERTICAL || xform == FLIP_HORIZONTAL) {
+ node.changeOptionsClockwise(!node.isOptionsClockwise());
+ }
+ }
+
+ // adjust line attributes
+ if (type.equals(LINE)) {
+ if (xform == FLIP_VERTICAL || xform == FLIP_HORIZONTAL ||
+ xform == ROTATE_90_CW || xform == ROTATE_90_CCW) {
+ node.setLineDirection(!node.getLineDirection());
+ }
+ }
+
+ // adjust linear bar graph attributes
+ if (type.equals(LINEARBARGRAPH)) {
+ if ((xform == FLIP_VERTICAL && node.isOptionsHorizontal()) ||
+ (xform == FLIP_HORIZONTAL && !node.isOptionsHorizontal()) ||
+ (xform == ROTATE_90_CW && node.isOptionsHorizontal()) ||
+ (xform == ROTATE_90_CCW && !node.isOptionsHorizontal()) ||
+ (xform == ROTATE_180)) {
+ node.changeOptionsGrowPositive(!node.isOptionsGrowPositive());
+ }
+
+ if (xform == ROTATE_90_CW || xform == ROTATE_90_CCW) {
+ node.changeOptionsHorizontal(!node.isOptionsHorizontal());
+ }
+ }
+
+ if (Utils.equals(type, CONTAINER, BUTTON, DATAMASK, ALARMMASK, KEY,
+ AUXILIARYFUNCTION, AUXILIARYINPUT, WORKINGSET)) {
+ // search through all childs and apply transformation for them
+ XMLTreeModel model = node.getModel();
+ for (int i = 0, n = node.getModel().getChildCount(node); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(node, i);
+
+ // e.g. data masks and alarm masks do not have widths
+ if (nd.getWidth() == null) {
+ continue;
+ }
+
+ // don't transform the active mask in working set (it does not have a width!)
+ //if (type.equals(WORKINGSET) &&
+ // (nd.getType().equals(DATAMASK) || nd.getType().equals(ALARMMASK))) {
+ // continue;
+ //}
+
+ // objects should not be rotated around the center point
+ int midXNew = midX;
+ int midYNew = midY;
+
+ if (Utils.equals(type, CONTAINER, BUTTON)) {
+ if (xform == ROTATE_90_CW) {
+ midXNew = midY;
+ }
+ if (xform == ROTATE_90_CCW) {
+ midYNew = midX;
+ }
+ }
+
+ // if object is a button, the inside width and height are 8 pixels smaller than outside
+ if (Utils.equals(type, BUTTON)) {
+ midXNew -= 4;
+ midYNew -= 4;
+ }
+
+ int width = nd.getWidth();
+ int height = (nd.getHeight() != null) ? nd.getHeight() : width;
+
+ // magic...
+ if (visitedElements.contains(nd.actual()) &&
+ (xform == ROTATE_90_CW || xform == ROTATE_90_CCW)) {
+ int temp = width;
+ width = height;
+ height = temp;
+ }
+
+ int newmidX = xformX(xform, midXNew, midYNew, nd.getX() + width / 2, nd.getY() + height / 2);
+ int newmidY = xformY(xform, midXNew, midYNew, nd.getX() + width / 2, nd.getY() + height / 2);
+
+ // apply the transformation only if the child has not yet been visited
+ if (!visitedElements.contains(nd.actual())) {
+ geometricTransformationRecursive(nd, xform, width / 2, height / 2, visitedElements);
+ }
+
+ int newHeight = (nd.getHeight() != null) ? nd.getHeight() : nd.getWidth();
+ int newWidth = nd.getWidth();
+
+ // transform position
+ nd.setX(newmidX - newWidth / 2);
+ nd.setY(newmidY - newHeight / 2);
+ }
+ }
+
+
+ // swap the width and height attributes, if transformation is a
+ // rotation, object can be rotated and it has width and height
+ if (((xform == ROTATE_90_CW) || (xform == ROTATE_90_CCW)) &&
+ Utils.equals(type, CONTAINER, ELLIPSE, LINEARBARGRAPH, POLYGON,
+ RECTANGLE, LINE, BUTTON)) {
+ int width = node.getWidth();
+ int height = (node.getHeight() != null) ? node.getHeight() : width;
+ node.setWidth(height);
+ node.setHeight(width);
+ }
+ }
+
+ private static int xformX(int xform, int midX, int midY, int x, int y) {
+ switch (xform) {
+ case FLIP_VERTICAL:
+ return 2 * midX - x;
+ case FLIP_HORIZONTAL:
+ return x;
+ case ROTATE_90_CW:
+ return midX - (y - midY);
+ case ROTATE_90_CCW:
+ return midX + (y - midY);
+ case ROTATE_180:
+ return 2 * midX - x;
+ default:
+ throw new RuntimeException("no such transform (" + xform + ")");
+ }
+ /*
+ if(transformation == 0)
+ return 2*midX - x;
+ if(transformation == 1)
+ return x;
+ if(transformation == 2)
+ return midX - (y-midY);
+ if(transformation == 3)
+ return midX + (y-midY);
+ if(transformation == 4)
+ return 2*midX - x;
+ else
+ return 0;
+ */
+ }
+
+ private static int xformY(int xform, int midX, int midY, int x, int y) {
+ switch (xform) {
+ case FLIP_VERTICAL:
+ return y;
+ case FLIP_HORIZONTAL:
+ return 2 * midY - y;
+ case ROTATE_90_CW:
+ return midY + (x - midX);
+ case ROTATE_90_CCW:
+ return midY - (x - midX);
+ case ROTATE_180:
+ return 2 * midY - y;
+ default:
+ throw new RuntimeException("no such transform (" + xform + ")");
+ }
+ /*
+ if(transformation == 0)
+ return y;
+ if(transformation == 1)
+ return 2*midY - y;
+ if(transformation == 2)
+ return midY + (x-midX);
+ if(transformation == 3)
+ return midY - (x-midX);
+ if(transformation == 4)
+ return 2*midY - y;
+ else
+ return 0;
+ */
+ }
+
+ public void showPopup(final MouseEvent e) {
+ popup.show(e.getComponent(), e.getX(), e.getY());
+ }
+}
diff --git a/src/pooledit/Utils.java b/src/pooledit/Utils.java
new file mode 100644
index 0000000..474d387
--- /dev/null
+++ b/src/pooledit/Utils.java
@@ -0,0 +1,160 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package pooledit;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import javax.imageio.ImageIO;
+import javax.swing.ImageIcon;
+
+/**
+ *
+ * @author mohman
+ */
+public class Utils {
+
+ /**
+ * Creates a new image by loading it from a file. Loading is done using
+ * URLConnection which can load images inside the jar package.
+ * @param path
+ * @return
+ */
+ static public ImageIcon createImageIcon(String path) {
+ ImageIcon icon = null;
+ URL url = Utils.class.getResource(path);
+ if (url != null) {
+ try {
+ URLConnection conn = url.openConnection();
+ BufferedImage img = ImageIO.read(conn.getInputStream());
+ icon = new ImageIcon(img);
+ }
+ catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return icon;
+ }
+
+ /**
+ * Checks whether the options string contain the specified option.
+ * Example: optionsContain("bold+invertedflashing", "inverted");
+ * @param options
+ * @param option
+ * @return
+ */
+ static public boolean optionsContain(String options, String option) {
+ int i = -1;
+ while ((i = options.indexOf(option, i + 1)) >= 0) {
+ if (i > 0 && options.charAt(i - 1) != '+') {
+ continue;
+ }
+ int pos = i + option.length();
+ int len = options.length();
+ if (pos < len && options.charAt(pos) != '+') {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Removes the specified option from the options list.
+ * @param options
+ * @param option
+ * @return
+ */
+ static public String optionsRemove(String options, String option) {
+ int i = -1;
+ while ((i = options.indexOf(option, i + 1)) >= 0) {
+ if (i > 0 && options.charAt(i - 1) != '+') {
+ continue;
+ }
+ int pos = i + option.length();
+ int len = options.length();
+ if (pos < len && options.charAt(pos) != '+') {
+ continue;
+ }
+ String s = (i > 0) ? options.substring(0, i - 1) : "";
+ String t = (pos < len) ? options.substring(pos + 1) : "";
+ return t.isEmpty() ? s : s.isEmpty() ? t : s + "+" + t;
+ }
+ return options;
+ }
+
+ /**
+ * Returns true, if at least one of the later values equals the first
+ * value.
+ * @param value
+ * @param values
+ * @return
+ */
+ public static boolean equals(String value, String ... values) {
+ for (int i = 0, n = values.length; i < n; i++) {
+ if (value.equals(values[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the index to the later value that is equal to the first
+ * value. If there is no such value, this method returns -1.
+ * @param name
+ * @param names
+ * @return
+ */
+ public static int indexEquals(String name, String ... names) {
+ for (int i = 0, n = names.length; i < n; i++) {
+ if (name.equals(names[i])) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Check whether two objects are equals. Unlike in o1.equals(o2), both
+ * objects can be null, equalObjects(null, null) return true.
+ * @param o1
+ * @param o2
+ * @return
+ */
+ public static boolean equalObjects(Object o1, Object o2) {
+ return (o1 == null && o2 == null) || (o1 != null && o1.equals(o2));
+ }
+
+ /*
+ public static void main(String[] args) {
+ System.out.println("Testing...");
+
+ System.out.println("optionsContain(ab+a, a): " + optionsContain("ab+a", "a"));
+ System.out.println("optionsContain(ab+c, a): " + optionsContain("ab+c", "a"));
+ System.out.println("optionsContain(ab+b, b): " + optionsContain("ab+b", "b"));
+ System.out.println("optionsContain(ab+c, b): " + optionsContain("ab+c", "b"));
+
+ System.out.println("optionsContain(a+ab, a): " + optionsContain("a+ab", "a"));
+ System.out.println("optionsContain(c+ab, a): " + optionsContain("c+ab", "a"));
+ System.out.println("optionsContain(b+ab, b): " + optionsContain("b+ab", "b"));
+ System.out.println("optionsContain(c+ab, b): " + optionsContain("c+ab", "b"));
+
+ System.out.println("optionsContain(ab+aa+a+ca, a): " + optionsContain("ab+aa+a+ca", "a"));
+ System.out.println("optionsContain(ab+c+aa+ca, a): " + optionsContain("ab+c+aa+ca", "a"));
+ }
+ */
+ /*
+ public static void main(String[] args) {
+ System.out.println("Testing...");
+
+ System.out.println("optionsRemove(ab+a, a): " + optionsRemove("ab+a", "a"));
+ System.out.println("optionsRemove(a+ab, a): " + optionsRemove("a+ab", "a"));
+ System.out.println("optionsRemove(ab+a+ca, a): " + optionsRemove("ab+a+ca", "a"));
+ System.out.println("optionsRemove(ba+a+ac, a): " + optionsRemove("ba+a+ac", "a"));
+ }
+ */
+}
diff --git a/src/schema/iso11783.xsd b/src/schema/iso11783.xsd
new file mode 100644
index 0000000..ed67fb9
--- /dev/null
+++ b/src/schema/iso11783.xsd
@@ -0,0 +1,1243 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/treemodel/XMLTreeModel.java b/src/treemodel/XMLTreeModel.java
new file mode 100644
index 0000000..66bd53b
--- /dev/null
+++ b/src/treemodel/XMLTreeModel.java
@@ -0,0 +1,1014 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package treemodel;
+
+import static pooledit.Definitions.*;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+import javax.swing.SwingUtilities;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+import org.w3c.dom.events.MutationEvent;
+import java.util.HashMap;
+import java.util.Map;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import pooledit.Tools;
+
+/**
+ * XML tree model, maps DOM to TreeModel interface
+ *
+ * @author mohman
+ */
+public class XMLTreeModel implements TreeModel, EventListener {
+
+ /** Category nodes for this model */
+ protected final XMLTreeNode[] CATEGORY_NODES = new XMLTreeNode[CATEGORIES.length];
+ /** Subcategory nodes for this model */
+ protected final XMLTreeNode[][] SUBCATEGORY_NODES = new XMLTreeNode[SUBCATEGORYGROUPS.length][];
+
+ private Document doc;
+ private XMLTreeNode root;
+ private Map nameMap;
+
+ private Map childMap;
+
+ /**
+ * Default constructor.
+ */
+ public XMLTreeModel() {
+ this.childMap = new HashMap();
+ }
+
+ /**
+ *
+ * @param doc
+ * @param nameMap
+ */
+ public XMLTreeModel(Document doc, Map nameMap) {
+ this.doc = doc;
+ this.nameMap = nameMap;
+ this.childMap = new HashMap();
+ this.root = createRootAndCategoryNodes();
+ }
+
+ /**
+ * Replaces the old document with a new one. Event listener are removed
+ * from the old document and added to the new one.
+ * @param doc
+ */
+ public void setDocument(Document doc) {
+ if (this.doc == doc) {
+ return;
+ }
+ if (this.doc != null) {
+ ((EventTarget) this.doc).removeEventListener("DOMNodeRemoved", this, false);
+ ((EventTarget) this.doc).removeEventListener("DOMNodeInserted", this, false);
+ ((EventTarget) this.doc).removeEventListener("DOMAttrModified", this, false);
+ }
+ ((EventTarget) doc).addEventListener("DOMNodeRemoved", this, false);
+ ((EventTarget) doc).addEventListener("DOMNodeInserted", this, false);
+ ((EventTarget) doc).addEventListener("DOMAttrModified", this, false);
+
+ this.doc = doc;
+ this.childMap.clear();
+ this.nameMap = Tools.createNameMap(doc);
+
+ this.root = createRootAndCategoryNodes();
+ fireTreeStructureChanged(new TreePath(root));
+ }
+
+ private XMLTreeNode createRootAndCategoryNodes() {
+ XMLTreeNode rt = XMLTreeNode.createRootNode(this, doc.getDocumentElement());
+
+ // create category nodes for this model
+ for (int i = 0, n = CATEGORIES.length; i < n; i++) {
+ CATEGORY_NODES[i] = rt.createTypeNode(CATEGORIES[i]);
+ }
+ // create subcategory nodes for this model
+ for (int j = 0, m = SUBCATEGORYGROUPS.length; j < m; j++) {
+ int n = SUBCATEGORYGROUPS[j].length;
+ SUBCATEGORY_NODES[j] = new XMLTreeNode[n];
+ for (int i = 0; i < n; i++) {
+ SUBCATEGORY_NODES[j][i] = rt.createTypeNode(SUBCATEGORYGROUPS[j][i]);
+ }
+ }
+ return rt;
+ }
+
+ /**
+ * Gets the document.
+ * @return
+ */
+ public Document getDocument() {
+ return doc;
+ }
+
+ /**
+ * Gets the name map.
+ * @return
+ */
+ public Map getNameMap() {
+ return nameMap;
+ }
+
+ /**
+ * Gets the element that has the specified name.
+ * @param name
+ * @return
+ */
+ public Element getElementByName(String name) {
+ return nameMap.get(name);
+ }
+
+ /**
+ * Gets a cached xml tree node list if one exists for this parent,
+ * otherwise creates a new one and returns it.
+ * @param parent
+ * @return
+ */
+ private XMLTreeNodeList getXMLTreeNodeList(Object parent) {
+ XMLTreeNodeList c = childMap.get(parent);
+ if (c == null) {
+ c = new XMLTreeNodeList(((XMLTreeNode) parent));
+ childMap.put((XMLTreeNode) parent, c);
+ }
+ return c;
+ }
+
+ //------------------------------------------------------------//
+
+ /**
+ * Gets the child of parent at index index in the parent's
+ * child array.
+ * @param parent
+ * @param index
+ * @return
+ */
+ @Override
+ public Object getChild(Object parent, int index) {
+ XMLTreeNodeList c = getXMLTreeNodeList(parent);
+ return c.get(index);
+ }
+
+ /**
+ * Gets the number of children of parent.
+ * @param parent
+ * @return
+ */
+ @Override
+ public int getChildCount(Object parent) {
+ XMLTreeNodeList c = getXMLTreeNodeList(parent);
+ return c.size();
+ }
+
+ /**
+ * Gets the index of child in parent.
+ * @param parent
+ * @param child
+ * @return
+ */
+ @Override
+ public int getIndexOfChild(Object parent, Object child) {
+ if (parent == null || child == null) {
+ return -1;
+ }
+ XMLTreeNodeList c = getXMLTreeNodeList(parent);
+ return c.indexOf(child);
+ }
+
+ /**
+ * Gets the root of the tree.
+ */
+ @Override
+ public Object getRoot() {
+ return root;
+ }
+
+ /**
+ * Returns true if node is a leaf.
+ * @param node
+ * @return
+ */
+ @Override
+ public boolean isLeaf(Object node) {
+ XMLTreeNodeList c = getXMLTreeNodeList(node);
+ return c.isEmpty();
+ }
+
+ /**
+ * Messaged when the user has altered the value for the item
+ * identified by path to newValue.
+ * @param path
+ * @param newValue
+ */
+ @Override
+ public void valueForPathChanged(TreePath path, Object newValue) {
+
+ }
+
+ //------------------------------------------------------------//
+
+ private Vector listeners = new Vector();
+
+ /**
+ * Adds a listener for the TreeModelEvent posted after the tree
+ * changes.
+ * @param l
+ */
+ @Override
+ public void addTreeModelListener(TreeModelListener l) {
+ if (!listeners.contains(l)) {
+ //System.out.println("ADDING TREE MODEL LISTENER: " + l);
+ listeners.add(l);
+ }
+ }
+
+ /**
+ * Removes a listener previously added with addTreeModelListener.
+ * @param l
+ */
+ @Override
+ public void removeTreeModelListener(TreeModelListener l) {
+ // System.out.println("REMOVING TREE MODEL LISTENER: " + l);
+ listeners.remove(l);
+ }
+
+ //------------------------------------------------------------//
+
+ /**
+ * Invoked after a node (or a set of siblings) has changed in some
+ * way.
+ * @param path
+ * @param childIndices
+ * @param children
+ */
+ private void fireTreeNodesChanged(TreePath path,
+ int[] childIndices,
+ Object[] children) {
+
+ TreeModelEvent e = new TreeModelEvent(this, path, childIndices,
+ children);
+ //System.out.println(" ---------- changed ---------- " + e);
+ for (TreeModelListener l : listeners) {
+ l.treeNodesChanged(e);
+ }
+ }
+
+ /**
+ * Invoked after nodes have been inserted into the tree.
+ * @param path
+ * @param childIndices
+ * @param children
+ */
+ private void fireTreeNodesInserted(TreePath path,
+ int[] childIndices,
+ Object[] children) {
+
+ TreeModelEvent e = new TreeModelEvent(this, path, childIndices,
+ children);
+ //System.out.println(" ---------- inserted ---------- " + e);
+ for (TreeModelListener l : listeners) {
+ l.treeNodesInserted(e);
+ }
+ }
+
+ /**
+ * Invoked after nodes have been removed from the tree.
+ * @param path
+ * @param childIndices
+ * @param children
+ */
+ private void fireTreeNodesRemoved(TreePath path,
+ int[] childIndices,
+ Object[] children) {
+
+ TreeModelEvent e = new TreeModelEvent(this, path, childIndices,
+ children);
+ //System.out.println(" ---------- removed ---------- " + e);
+
+ // the listeners vector might change as a result of calling
+ // treeNodesRemoved
+ Vector temp = new Vector(listeners);
+ for (TreeModelListener l : temp) {
+ l.treeNodesRemoved(e);
+ }
+ }
+
+ /**
+ * Invoked after the tree has drastically changed structure from a
+ * given node down.
+ * @param path
+ */
+ private void fireTreeStructureChanged(TreePath path) {
+ TreeModelEvent e = new TreeModelEvent(this, path);
+ //System.out.println(" ---------- structure ---------- " + e);
+ for (TreeModelListener l : listeners) {
+ l.treeStructureChanged(e);
+ }
+ }
+
+ //------------------------------------------------------------//
+
+ /*
+ public Map getMapCopy() {
+ // we really need a deep copy!
+ Map newMap = new HashMap();
+ for (XMLTreeNode key : childMap.keySet()) {
+ XMLTreeNodeList list = childMap.get(key);
+ newMap.put(key, new XMLTreeNodeList(list));
+ }
+ return newMap;
+ }
+ */
+ static public void showList(String msg, List l) {
+ System.out.print(msg + ":");
+ for (int i = 0, n = l.size(); i < n; i++) {
+ System.out.print(" " + l.get(i));
+ }
+ System.out.println();
+ }
+
+ static public void compLists(List l1, List l2) {
+ for (int i = 0, n = l1.size(); i < n; i++) {
+ if (l1.get(i) == l2.get(i)) {
+ System.out.print(" " + l1.get(i));
+ } else {
+ System.out.print(" ####" + l1.get(i) + "#### ");
+ }
+ }
+ System.out.println();
+ }
+
+ public void fixModel() {
+ Tools.updateNameMap(doc, nameMap);
+ XMLTreeModel newModel = new XMLTreeModel(doc, nameMap);
+ fixModel(newModel, new TreePath(root));
+ }
+
+ public void fixModel(XMLTreeModel newModel, TreePath parentPath) {
+ XMLTreeNode parent = (XMLTreeNode) parentPath.getLastPathComponent();
+
+ List oldChildren = getXMLTreeNodeList(parent);
+ List newChildren = newModel.getXMLTreeNodeList(parent);
+
+ /*
+ System.out.println("FIXING: " + parent);
+ showList("old: ", oldChildren);
+ showList("new: ", newChildren);
+ */
+
+ List toBeRemoved = new ArrayList();
+ toBeRemoved.addAll(oldChildren);
+ toBeRemoved.removeAll(newChildren);
+
+ List toBeInserted = new ArrayList();
+ toBeInserted.addAll(newChildren);
+ toBeInserted.removeAll(oldChildren);
+
+ List toBeRetained = new ArrayList();
+ toBeRetained.addAll(newChildren);
+ toBeRetained.retainAll(oldChildren);
+ //toBeRetained.addAll(oldChildren);
+ //toBeRetained.retainAll(newChildren);
+
+ // find out what nodes must be removed and then inserted
+ // to get all nodes in the proper order
+ int size = toBeRetained.size();
+ BitSet bits = new BitSet(size * size);
+ for (int i = size - 1; i >= 0; i--) {
+ for (int j = i - 1; j >= 0; j--) {
+ if (!haveSameOrder(toBeRetained.get(i), toBeRetained.get(j),
+ oldChildren, newChildren)) {
+
+ // the relation is symmetric
+ bits.set(i + size * j);
+ bits.set(size * i + j);
+ }
+ }
+ }
+
+ do {
+ int maxVal = 0;
+ int maxIndex = 0;
+ for (int i = 0; i < size; i++) {
+ int start = size * i;
+ int end = start + size;
+ BitSet row = bits.get(start, end);
+ int val = row.cardinality();
+ if (val > maxVal) {
+ maxVal = val;
+ maxIndex = i;
+ }
+ }
+ if (maxVal > 0) {
+ // clear bit row
+ int start = size * maxIndex;
+ int end = start + size;
+ bits.clear(start, end);
+ // clear bit col
+ for (int i = 0; i < size; i++) {
+ bits.clear(maxIndex + size * i);
+ }
+ XMLTreeNode wrongOrder = toBeRetained.remove(maxIndex);
+ // System.out.println("REORDERING: " + wrongOrder);
+ toBeRemoved.add(wrongOrder);
+ toBeInserted.add(wrongOrder);
+ }
+ }
+ while (bits.cardinality() > 0);
+
+ /*
+ // they may look the same (for the tree model), but they are not...
+ if (toBeRetained.size() > 0) {
+ for (int i = 0, n = toBeRetained.size(); i < n; i++) {
+ XMLTreeNode object = toBeRetained.get(i);
+ oldChildren.set(oldChildren.indexOf(object), object);
+ }
+ }
+ */
+
+ // remove nodes
+ if (toBeRemoved.size() > 0) {
+ int[] removedIndices = new int[toBeRemoved.size()];
+ for (int i = 0, n = removedIndices.length; i < n; i++) {
+ removedIndices[i] = oldChildren.indexOf(toBeRemoved.get(i));
+ }
+ oldChildren.removeAll(toBeRemoved);
+ fireTreeNodesRemoved(parentPath, removedIndices, toBeRemoved.toArray());
+ }
+
+ // insert nodes
+ if (toBeInserted.size() > 0) {
+
+ int[] insertedIndices = new int[toBeInserted.size()];
+ Map insertMap = new HashMap();
+ for (int i = 0, n = insertedIndices.length; i < n; i++) {
+ XMLTreeNode object = toBeInserted.get(i);
+ int index = newChildren.indexOf(object);
+ insertedIndices[i] = index;
+ insertMap.put(index, object);
+ }
+
+ // indices must be sorted for this operation!
+ Arrays.sort(insertedIndices);
+ for (int i = 0, n = insertedIndices.length; i < n; i++) {
+ // order counts here -> cannot say just addAll(...)
+ int index = insertedIndices[i];
+ oldChildren.add(index, insertMap.get(index));
+ }
+ fireTreeNodesInserted(parentPath, insertedIndices, toBeInserted.toArray());
+ }
+
+ /*
+ System.out.print("LIST ARE EQUAL: " +
+ oldChildren.equals(newChildren));
+ compLists(oldChildren, newChildren);
+ */
+
+ // continue with children
+ for (int i = 0, n = oldChildren.size(); i < n; i++) {
+ Object child = oldChildren.get(i);
+ fixModel(newModel, parentPath.pathByAddingChild(child));
+ }
+ }
+
+ /**
+ * Returns true if the elements a and b are in the same order
+ * in the lists p and q.
+ */
+ private static boolean haveSameOrder(Object a, Object b,
+ List> p, List> q) {
+ int v1 = p.indexOf(a) - p.indexOf(b);
+ int v2 = q.indexOf(a) - q.indexOf(b);
+ return (v1 < 0 && v2 < 0) || (v1 > 0 && v2 > 0);
+ }
+
+ //------------------------------------------------------------//
+
+ /**
+ * This code does not execute in the GUI thread!!!
+ */
+ @Override
+ public void handleEvent(Event evt) {
+ final MutationEvent mev = (MutationEvent) evt;
+ final Element target = (Element) mev.getTarget();
+
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ String type = mev.getType();
+ if (type.equals("DOMNodeRemoved")) {
+ //System.out.println("REMOVING: " + target.getAttribute(NAME));
+ changeAttribute(target, NAME, "", target.getAttribute(NAME));
+ } else if (type.equals("DOMNodeInserted")) {
+ //System.out.println("INSERTING: " + target.getAttribute(NAME));
+ changeAttribute(target, NAME, target.getAttribute(NAME), "");
+ }
+ if (type.equals("DOMAttrModified")) {
+ String name = mev.getAttrName();
+ String newValue = mev.getNewValue();
+ String oldValue = mev.getPrevValue(); // can be null
+ changeAttribute(target, name, newValue, oldValue);
+ if (!name.equals(NAME)) {
+ return;
+ }
+ }
+
+ fixModel();
+ }
+ });
+ }
+
+ /**
+ * Walks through the tree and changes the attributes of every instance of
+ * the element.
+ */
+ private void changeAttribute(Element elem, String name, String newValue, String oldValue) {
+ changeAttribute(elem, name, newValue, oldValue, new TreePath(root));
+ }
+ private void changeAttribute(Element elem, String name, String newValue, String oldValue, TreePath path) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+
+ // update model lazily
+ // XMLTreeNodeList c = childMap.get(node);
+ XMLTreeNodeList c = this.getXMLTreeNodeList(node);
+ if (c == null) {
+ return;
+ }
+ for (int i = c.size() - 1; i >= 0; i--) {
+ XMLTreeNode nd = c.get(i);
+ Element actual = nd.actual();
+ Element link = nd.link();
+ // it makes no difference whether the node is link or actual object
+ if (link == elem || actual == elem) {
+ // XMLTreeNodes are immutable, so to change links we have
+ // to remove the node and create a new one (it is done
+ // automatically in fixModel())
+ if (name.equals(NAME)) {
+ c.remove(nd);
+ fireTreeNodesRemoved(path, new int[] {i}, new Object[] {nd});
+ } else {
+ // this node has somehow changed (i.e. name)
+ fireTreeNodesChanged(path, new int[] {i}, new Object[] {nd});
+ }
+ }
+ // name attribute needs special attention
+ else if (name.equals(NAME)) {
+ // a working link that needs an update
+ if (link != null) {
+ // someone has removed name attribute
+ if (newValue == null) {
+ // nothing to do here
+ }
+ // this link (possibly) now points to this object (whose name has changed)
+ else if (!newValue.isEmpty() && link.getAttribute(NAME).equals(newValue)) {
+ c.remove(nd);
+ fireTreeNodesRemoved(path, new int[] {i}, new Object[] {nd});
+ }
+ // this link now (possibly) no longer points to this object (whose name has changed)
+ else if (oldValue != null && !oldValue.isEmpty() && link.getAttribute(NAME).equals(oldValue)) {
+ c.remove(nd);
+ fireTreeNodesRemoved(path, new int[] {i}, new Object[] {nd});
+ }
+ }
+ // a broken link that needs fixing
+ else if (actual != null && nd.isType(INCLUDE_OBJECT) &&
+ !newValue.isEmpty() && actual.getAttribute(NAME).equals(newValue)) {
+ c.remove(nd);
+ fireTreeNodesRemoved(path, new int[] {i}, new Object[] {nd});
+ }
+ }
+ }
+ for (XMLTreeNode n : c) {
+ changeAttribute(elem, name, newValue, oldValue, path.pathByAddingChild(n));
+ }
+ }
+
+ /*
+ public void handleEvent(Event evt) {
+ try {
+ // FIXME: this is probably done too often, but this operation is
+ // relatively cheap anyways...
+ Tools.updateNameMap(doc, nameMap);
+
+ // NOTE: this code seems to be "brain damaged" i.e. there are
+ // separate cases for links and objects, it may have something to
+ // do with the fact that attribute name can mean two things:
+ // 1) it can be a name of an object or 2) it can be a reference
+ // to an object.
+ MutationEvent mev = (MutationEvent) evt;
+ Element child = (Element) mev.getTarget();
+ String type = mev.getType();
+ String name = child.getNodeName();
+
+ if (type.equals("DOMNodeRemoved")) {
+ Element parent = (Element) mev.getRelatedNode();
+ if (name.equals(INCLUDE_OBJECT) && nameMap.containsKey(name)) {
+ System.out.println("REMOVE LINK (" + parent + ", " + child + ")");
+ removeLink(parent, child);
+ }
+ else {
+ System.out.println("REMOVE OBJECT (" + parent + ", " + child + ")");
+ removeLinks(child);
+ removeObject(child);
+ }
+
+ // this is quite radical action, but removing objects (with
+ // names) can break existing links anywhere in the document
+ regenerateAll(new TreePath(root));
+ }
+ else if (type.equals("DOMNodeInserted")) {
+ Element parent = (Element) mev.getRelatedNode();
+ if (name.equals(INCLUDE_OBJECT) && nameMap.containsKey(name)) {
+ System.out.println("INSERT LINK (" + parent + ", " + child + ")");
+ insertLink(parent);
+ }
+ else {
+ System.out.println("INSERT OBJECT (" + parent + ", " + child + ")");
+ insertLink(parent);
+ }
+ }
+ else if (type.equals("DOMAttrModified")) {
+ System.out.println("CHANGE ATTRIBUTE (" + child + ": " + mev.getAttrName() + " -> " + mev.getNewValue() + ")");
+ // if the name attribute of a link object is changed we have to
+ // make sure that the link object is not using the old mapping
+ // (actual) anymore
+ if (mev.getAttrName().equals(NAME)) {
+ System.out.println("### " + getClass().getName() + " name changed from " + mev.getPrevValue() +
+ " to " + mev.getNewValue());
+ //updateName(child);
+
+ // this is quite radical action, but changing names can both
+ // create new links and break existing ones...
+ regenerateAll(new TreePath(root));
+ // regenerateChildNodes(new TreePath(root));
+ }
+
+ changeAttributes(child);
+ }
+ else {
+ System.out.println("SOMETHING ELSE: " + type);
+ }
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ */
+
+ /**
+ * Walks through the tree and removes all (links to) child elements
+ * from the specified parent element.
+ private void removeLink(Element parent, Element child) {
+ removeLink(parent, child, new TreePath(root));
+ }
+ private void removeLink(Element parent, Element child, TreePath path) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+
+ // update model lazily
+ XMLTreeNodeList c = childMap.get(node);
+ if (c == null) {
+ return;
+ }
+ if (node.actual() == parent) {
+ for (int i = c.size() - 1; i >= 0; i--) {
+ XMLTreeNode n = c.get(i);
+ if (n.link() == child) {
+ c.remove(i);
+ fireTreeNodesRemoved(path, new int[] {i}, new Object[] {n});
+ }
+ }
+ }
+ for (XMLTreeNode n : c) {
+ removeLink(parent, child, path.pathByAddingChild(n));
+ }
+ }
+ */
+
+ /**
+ * Regenerates all child nodes of every instance of the parent.
+ private void insertLink(Element parent) {
+ insertLink(parent, new TreePath(root));
+ }
+ private void insertLink(Element parent, TreePath path) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+
+ // update model lazily
+ XMLTreeNodeList c = childMap.get(node);
+ if (c == null) {
+ return;
+ }
+ if (node.actual() == parent) {
+ // redo child list
+ regenerateChildNodes(path);
+ }
+ for (XMLTreeNode n : c) {
+ insertLink(parent, path.pathByAddingChild(n));
+ }
+ }
+ */
+
+ /**
+ * Updates element name.
+ private void updateName(Element element) {
+ updateName(element, new TreePath(root));
+ }
+ private boolean updateName(Element element, TreePath path) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+
+ // update model lazily
+ XMLTreeNodeList c = childMap.get(node);
+ if (c == null) {
+ return false;
+ }
+ if (node.actual() == element || node.link() == element) {
+ // redo parents child list
+ regenerateChildNodes(path.getParentPath());
+ return true;
+ }
+ boolean loop = true;
+ while (loop) {
+ loop = false;
+ for (XMLTreeNode n : c) {
+ if (updateName(element, path.pathByAddingChild(n))) {
+ loop = true;
+ break;
+ }
+ }
+ }
+ return false;
+ }
+ */
+
+ /**
+ * Walks through the tree and removes all (links to) child elements
+ * regardless of the parent element
+ private void removeLinks(Element child) {
+ removeLinks(child, new TreePath(root));
+ }
+ private void removeLinks(Element child, TreePath path) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+
+ // update model lazily
+ XMLTreeNodeList c = childMap.get(node);
+ if (c == null) {
+ return;
+ }
+ for (int i = c.size() - 1; i >= 0; i--) {
+ XMLTreeNode n = c.get(i);
+ if (n.link() == child) {
+ c.remove(i);
+ fireTreeNodesRemoved(path, new int[] {i}, new Object[] {n});
+ }
+ }
+ for (XMLTreeNode n : c) {
+ removeLinks(child, path.pathByAddingChild(n));
+ }
+ }
+ */
+
+ /**
+ * Walks through the tree and removes the (actual) child element.
+ * The child element should be unique, however the whole tree is always
+ * checked.
+ private void removeObject(Element child) {
+ removeObject(child, new TreePath(root));
+ }
+ private void removeObject(Element child, TreePath path) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+
+ // update model lazily
+ XMLTreeNodeList c = childMap.get(node);
+ if (c == null) {
+ return;
+ }
+ for (int i = c.size() - 1; i >= 0; i--) {
+ XMLTreeNode n = c.get(i);
+ // do not remove links!
+ if (n.link() == null && n.actual() == child) {
+ c.remove(i);
+ fireTreeNodesRemoved(path, new int[] {i}, new Object[] {n});
+ }
+ }
+ for (XMLTreeNode n : c) {
+ removeObject(child, path.pathByAddingChild(n));
+ }
+ }
+ */
+
+ /**
+ * Walks through the tree and changes the attributes of every instance of
+ * the child element.
+ private void changeAttributes(Element child) {
+ changeAttributes(child, new TreePath(root));
+ }
+ private void changeAttributes(Element child, TreePath path) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+
+ // update model lazily
+ // XMLTreeNodeList c = childMap.get(node);
+ XMLTreeNodeList c = this.getXMLTreeNodeList(node);
+ if (c == null) {
+ return;
+ }
+ for (int i = 0, n = c.size(); i < n; i++) {
+ XMLTreeNode nd = c.get(i);
+ // it makes no difference whether the node is link or actual object
+ if (nd.link() == child || nd.actual() == child) {
+ // this node has somehow changed (i.e. name)
+ fireTreeNodesChanged(path, new int[] {i}, new Object[] {nd});
+ // the attribute nodes may also have been changed
+ regenerateChildNodes(path.pathByAddingChild(nd));
+ }
+ }
+ for (XMLTreeNode n : c) {
+ changeAttributes(child, path.pathByAddingChild(n));
+ }
+ }
+ */
+
+ /*
+ private void regenerateAll(TreePath path) {
+ regenerateChildNodes(path);
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+
+ XMLTreeNodeList c = childMap.get(node);
+ if (c == null) {
+ return;
+ }
+ for (XMLTreeNode n : c) {
+ regenerateAll(path.pathByAddingChild(n));
+ }
+ }
+ */
+
+ /**
+ * This method replaces the XMLTreeNodeList in the childMap for the last
+ * path element. It then removes the old nodes from the tree and adds the
+ * new nodes. This is better than just firing tree structure changed
+ * event because it will collapse that part of the JTree.
+ private void regenerateChildNodes(final TreePath path) {
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+
+ // be as lazy as possible
+ XMLTreeNodeList list = childMap.get(node);
+ if (list == null) {
+ return;
+ }
+
+ // some special cases
+ if (node.isType(OBJECTPOOL) || node.isType(CATEGORIES)) {
+ for (int i = 0, n = getChildCount(node); i < n; i++) {
+ regenerateChildNodes(path.pathByAddingChild(getChild(node, i)));
+ }
+ return;
+ }
+
+ // remove old nodes from the tree
+ int[] indices = new int[list.size()];
+ for (int i = 0, n = indices.length; i < n; i++) {
+ indices[i] = i;
+ }
+ fireTreeNodesRemoved(path, indices, list.toArray());
+
+ // insert new nodes to the tree
+ indices = new int[list.size()];
+ for (int i = 0, n = indices.length; i < n; i++) {
+ indices[i] = i;
+ }
+ fireTreeNodesInserted(path, indices, list.toArray());
+ }
+ */
+ /*
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+ XMLTreeNodeList newlist = new XMLTreeNodeList(node);
+ XMLTreeNodeList oldlist = childMap.put(node, newlist);
+
+ // remove old nodes from the tree
+ if (oldlist != null) {
+ int[] oldindices = new int[oldlist.size()];
+ for (int i = 0, n = oldlist.size(); i < n; i++) {
+ oldindices[i] = i;
+ }
+ fireTreeNodesRemoved(path.getPath(), oldindices, oldlist.toArray());
+ }
+
+ // insert new nodes to the tree
+ if (newlist != null) {
+ int[] newindices = new int[newlist.size()];
+ for (int i = 0, n = newlist.size(); i < n; i++) {
+ newindices[i] = i;
+ }
+ fireTreeNodesInserted(path.getPath(), newindices, newlist.toArray());
+ }
+ */
+
+
+ /*
+ static public int[] removeAll(List where, List what) {
+ List indices = new ArrayList();
+ for (int i = 0, n = what.size(); i < n; i++) {
+ int index = where.indexOf(what.get(i));
+ if (index >= 0) {
+ indices.add(index);
+ }
+ }
+ int[] rv = new int[indices.size()];
+ for (int i = 0, n = indices.size(); i < n; i++) {
+ rv[i] = indices.get(i);
+ }
+ return rv;
+ }
+
+ static public void showList(String name, List list) {
+ System.out.print(name + ":");
+ for (Object o : list) {
+ System.out.print(" " + o);
+ }
+ System.out.println();
+ }
+
+ static public int[] getAllIndices(List list) {
+ int[] indices = new int[list.size()];
+ for (int i = 0, n = indices.length; i < n; i++) {
+ indices[i] = i;
+ }
+ return indices;
+ }
+ */
+
+ /**
+ * Tries to convert string path into TreePath of XMLTreeNodes.
+ */
+ public TreePath findPathByPath(String pathStr) {
+ return findPathByPath(pathStr, new TreePath(root));
+ }
+ /**
+ * This code is certainly not nice. The extra complexity is caused by
+ * non-real (category) nodes in the xml node tree.
+ */
+ private TreePath findPathByPath(String pathStr, TreePath path) {
+
+ // ending condition for the recursion
+ if (pathStr == null || pathStr.equals("")) {
+ return path;
+ }
+
+ // paths are of the form "/container/button/label", in which case
+ // beginning should be "container" and ending "/button/label", the
+ // special case is "/label", in which case beginning should be
+ // "label" and ending "" or null.
+ int index = pathStr.indexOf('/', 1);
+ String beginning = index < 0 ? pathStr.substring(1) : pathStr.substring(1, index);
+ String ending = index < 0 ? null : pathStr.substring(index);
+
+ XMLTreeNode node = (XMLTreeNode) path.getLastPathComponent();
+ XMLTreeNodeList c = getXMLTreeNodeList(node);
+ for (XMLTreeNode n : c) {
+ if (n.isType(OBJECTS)) {
+ // real object has a name and it must match
+ if (n.getName().equals(beginning)) {
+ return findPathByPath(ending, path.pathByAddingChild(n));
+ }
+ }
+ else {
+ // non-real objects do not have a name (and are not in the path)
+ // - we must check them all
+ TreePath p = findPathByPath(pathStr, path.pathByAddingChild(n));
+ if (p != null) {
+ return p;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/treemodel/XMLTreeNode.java b/src/treemodel/XMLTreeNode.java
new file mode 100644
index 0000000..d48844e
--- /dev/null
+++ b/src/treemodel/XMLTreeNode.java
@@ -0,0 +1,1225 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package treemodel;
+
+import java.awt.Graphics2D;
+import static pooledit.Definitions.*;
+import javax.swing.tree.TreePath;
+import java.awt.Paint;
+import java.awt.Polygon;
+import java.awt.TexturePaint;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import javax.imageio.ImageIO;
+import color.ColorPalette;
+import objectview.LineAttributes;
+import font.BitmapFont;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.util.StringTokenizer;
+import org.w3c.dom.Element;
+import pooledit.PictureConverter;
+
+/**
+ * JTree sees XMLTreeNodes, override toString method to change the
+ * textual presentation. NOTE: the textual presentation can also be
+ * changed in the renderer; however, creating wrapper objects is a
+ * good idea (because we are showing a graph as a tree)
+ *
+ * @author mohman
+ */
+public class XMLTreeNode {
+
+ private final XMLTreeModel model;
+ private final XMLTreeNode parent;
+ private final TreePath path; // of elements
+
+ private final Element actual;
+ private final Element link; // this is the link node, if any
+ private final String type; // for category nodes for which node == null and link == null
+
+ private XMLTreeNode(XMLTreeModel model, XMLTreeNode parent, TreePath path,
+ Element actual, Element link, String type) {
+ this.model = model;
+ this.parent = parent;
+ this.path = path;
+ this.actual = actual;
+ this.link = link;
+ this.type = type;
+ }
+
+ /**
+ * Creates a root node.
+ */
+ protected static XMLTreeNode createRootNode(XMLTreeModel model, Element actual) {
+ return new XMLTreeNode(model, null, new TreePath(actual), actual, null, null);
+ }
+
+ protected XMLTreeNode createChildNode(Element node) {
+ Element actual = findActualElement(node);
+ Element link = null;
+ // node was not an actual node? -> it must have been a link node
+ if (actual == null) {
+ actual = node;
+ }
+ else if (actual != node) {
+ link = node;
+ }
+ return new XMLTreeNode(this.getModel(), this,
+ this.getPath().pathByAddingChild(link != null ? link : actual),
+ actual, link, null);
+ }
+
+ protected XMLTreeNode createTypeNode(String type) {
+ return new XMLTreeNode(this.getModel(), this,
+ this.getPath().pathByAddingChild(type),
+ null, null, type);
+ }
+
+ private Element findActualElement(Element node) {
+ if (node != null && node.getNodeName().equals(INCLUDE_OBJECT)) {
+ String name = node.getAttribute(NAME);
+
+ node = findActualElement(model.getNameMap().get(name));
+ }
+ return node;
+ }
+
+ public XMLTreeModel getModel() {
+ return model;
+ }
+
+ public Element actual() {
+ return actual;
+ }
+
+ public Element link() {
+ return link;
+ }
+
+ public Element effective() {
+ return link != null ? link : actual;
+ }
+
+ public boolean checkLink() {
+ return (link == null) ? true : actual == model.getNameMap().get(link.getAttribute(NAME));
+ }
+
+ public TreePath getPath() {
+ return path;
+ }
+
+ /**
+ * This returns the type of a category node, otherwise returns null.
+ * DO NOT use with other nodes!
+ */
+ public String type() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof XMLTreeNode)) {
+ return false;
+ }
+ XMLTreeNode other = (XMLTreeNode) o;
+ return this.path.equals(other.path);
+ }
+
+ @Override
+ public int hashCode() {
+ return path.hashCode();
+ }
+ /**
+ * Kludge
+ static public boolean similar(XMLTreeNode n1, XMLTreeNode n2) {
+ if (n1.equals(n2)) {
+ return true;
+ }
+ boolean b0 = n1.link() != null || n1.type() != null;
+ boolean b1 = (n1.link() == null) || n1.link().equals(n2.link());
+ boolean b2 = (n1.type() == null) || n1.type().equals(n2.type());
+ return (b0 && b1 && b2) || (n1.actual() != null && n1.link() == null && n1.actual().getAttribute(NAME).equals(n2.actual().getAttribute(NAME)));
+ }
+ */
+
+ /**
+ * This method checks whether the name of the node is one of the
+ * parameters.
+ */
+ public boolean isType(String ... names) {
+ String s = getType();
+ for (int i = 0, n = names.length; i < n; i++) {
+ if (s.equals(names[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method returns the name of the node, e.g. "objectpool".
+ */
+ public String getType() {
+ return actual == null ? type : actual.getNodeName();
+ }
+ /**
+ * This method returns the value of the "name" attribute.
+ */
+ public String getName() {
+ return actual != null ? actual.getAttribute(NAME) :
+ link != null ? link.getAttribute(NAME) : "N/A";
+ }
+ /**
+ * This method returns the value of the "role" attribute.
+ */
+ public String getRole() {
+ return link != null ? link.getAttribute(ROLE) :
+ actual != null ? actual.getAttribute(ROLE) : "N/A";
+ }
+ /**
+ * This method returns the value of the "pos_x" attribute corrected by
+ * other attributes that affect it (such as block_col).
+ */
+ public Integer getX() {
+ Element src = (link == null) ? actual : link;
+ String pos_x = src.getAttribute(POS_X);
+ if (pos_x.isEmpty()) {
+ return null;
+ }
+ int x = parseInt(pos_x);
+
+ String block_col = src.getAttribute(BLOCK_COL);
+ String block_font = src.getAttribute(BLOCK_FONT);
+
+ if (!block_col.isEmpty() && !block_font.isEmpty()) {
+
+ Element font = model.getElementByName(block_font);
+ if (font != null) {
+ Dimension dim = BitmapFont.nameToDimension(font.getAttribute(FONT_SIZE));
+ x += parseInt(block_col) * dim.width;
+ }
+ }
+ return x;
+ }
+ /**
+ * This method returns the value of the "pos_y" attribute corrected by
+ * other attributes that affect it (such as block_row).
+ */
+ public Integer getY() {
+ Element src = (link == null) ? actual : link;
+ String pos_y = src.getAttribute(POS_Y);
+ if (pos_y.isEmpty()) {
+ return null;
+ }
+ int y = parseInt(pos_y);
+
+ String block_row = src.getAttribute(BLOCK_ROW);
+ String block_font = src.getAttribute(BLOCK_FONT);
+
+ if (!block_row.isEmpty() && !block_font.isEmpty()) {
+
+ Element font = model.getElementByName(block_font);
+ if (font != null) {
+ Dimension dim = BitmapFont.nameToDimension(font.getAttribute(FONT_SIZE));
+ y += parseInt(block_row) * dim.height;
+ }
+ }
+ return y;
+ }
+
+ /**
+ * This method changes the pos_x attribute
+ * FIXME should this be corrected somehow
+ */
+ public void setX(int x) {
+ Element src = (link == null) ? actual : link;
+ src.setAttribute(POS_X, Integer.toString(x));
+ }
+
+ /**
+ * This method changes the pos_x attribute
+ * FIXME should this be corrected somehow
+ */
+ public void setY(int y) {
+ Element src = (link == null) ? actual : link;
+ src.setAttribute(POS_Y, Integer.toString(y));
+ }
+
+ /**
+ * This method returns the value of the "number_of_decimals" attribute.
+ */
+ public Integer getNumberOfDecimals(){
+ String d = actual.getAttribute(NUMBER_OF_DECIMALS);
+ return d.isEmpty() ? 0 : parseInt(d);
+ }
+ /**
+ * This method returns the value of the "width" attribute.
+ */
+ public Integer getWidth() {
+ String w = actual.getAttribute(WIDTH);
+ return w.isEmpty() ? null : parseInt(w);
+ }
+ /**
+ * This method changes the value of the "width" attribute.
+ */
+ public void setWidth(int width) {
+ actual.setAttribute(WIDTH, Integer.toString(width));
+ }
+ /**
+ * This method returns the value of the "height" attribute.
+ */
+ public Integer getHeight() {
+ String h = actual.getAttribute(HEIGHT);
+ return h.isEmpty() ? null : parseInt(h);
+ }
+ /**
+ * This method changes the value of the "height" attribute.
+ */
+ public void setHeight(int height) {
+ actual.setAttribute(HEIGHT, Integer.toString(height));
+ }
+ /**
+ * This method returns the value of the "start_angle" attribute.
+ */
+ public Integer getStartAngle() {
+ String w = actual.getAttribute(START_ANGLE);
+ return w.isEmpty() ? null : parseInt(w);
+ }
+ /**
+ * This method changes the value of the "start_angle" attribute.
+ */
+ public void setStartAngle(int angle) {
+ actual.setAttribute(START_ANGLE, Integer.toString(angle));
+ }
+ /**
+ * This method returns the value of the "end_angle" attribute.
+ */
+ public Integer getEndAngle() {
+ String h = actual.getAttribute(END_ANGLE);
+ return h.isEmpty() ? null : parseInt(h);
+ }
+ /**
+ * This method changes the value of the "end_angle" attribute.
+ */
+ public void setEndAngle(int angle) {
+ actual.setAttribute(END_ANGLE, Integer.toString(angle));
+ }
+ /**
+ * This method returns the value of the "line_direction" attribute.
+ */
+ public Boolean getLineDirection() {
+ String d = actual.getAttribute(LINE_DIRECTION);
+ if (d.isEmpty() || d.equals("0") || d.equals("toplefttobottomright")) {
+ return false;
+ }
+ else if (d.equals("1") || d.equals("bottomlefttotopright")) {
+ return true;
+ }
+ else {
+ return null;
+ }
+ }
+ /**
+ * This method sets the value of the "line_direction" attribute.
+ */
+ public void setLineDirection(boolean lineDirection) {
+ if( lineDirection && !getLineDirection())
+ actual.setAttribute(LINE_DIRECTION, "bottomlefttotopright");
+ if( !lineDirection && getLineDirection())
+ actual.setAttribute(LINE_DIRECTION, "toplefttobottomright");
+ }
+
+ /**
+ * This method returns the value of the "actual_width" attribute.
+ * FIXME: this is never used, actual witdh is read from the file?
+ */
+ public Integer getActualWidth() {
+ String w = actual.getAttribute("actual_width");
+ return w.isEmpty() ? null : parseInt(w);
+ }
+ /**
+ * This method returns the value of the "actual_height" attribute.
+ * FIXME: this is never used, actual height is read from the file?
+ */
+ public Integer getActualHeight() {
+ String h = actual.getAttribute("actual_height");
+ return h.isEmpty() ? null : parseInt(h);
+ }
+ /**
+ * This method returns the value of the "dimension" attribute of the
+ * objectpool root element.
+ */
+ public Integer getDimension() {
+ String d = actual.getAttribute(DIMENSION);
+ return d.isEmpty() ? null : parseInt(d);
+ }
+ /**
+ * This method returns the value of the "sk_width" attribute of the
+ * objectpool root element.
+ */
+ public Integer getSKWidth() {
+ String s = actual.getAttribute(SK_WIDTH);
+ return s.isEmpty() ? null : parseInt(s);
+ }
+ /**
+ * This method returns the value of the "sk_height" attribute of the
+ * objectpool root element.
+ */
+ public Integer getSKHeight() {
+ String s = actual.getAttribute(SK_HEIGHT);
+ return s.isEmpty() ? null : parseInt(s);
+ }
+ /**
+ * This method returns the value of the "fix_bitmap_path" attribute of the
+ * objectpool root element.
+ */
+ public String getFixBitmapPath() {
+ String f = actual.getAttribute(FIX_BITMAP_PATH);
+ return f.isEmpty() ? null : f; // FIXME: use a more decent default value?
+ }
+ /**
+ * This method returns the value of the "std_bitmap_path" attribute of the
+ * objectpool root element.
+ */
+ public String getStdBitmapPath() {
+ String s = actual.getAttribute(STD_BITMAP_PATH);
+ return s.isEmpty() ? null : s; // FIXME: use a more decent default value?
+ }
+ /**
+ * This method determines what text is shown in the tree view.
+ */
+ public String toString() {
+ if (type != null) {
+ return type;
+ }
+ String base = getName();
+ String type = getType();
+
+ if (type == null) {
+ // do nothing
+ }
+ else if (type.equals(OBJECTPOOL)) {
+ base = type;
+ }
+ else if (type.equals(LANGUAGE)) {
+ base = type + ": " + getCode();
+ }
+ else if (type.equals(POINT)) {
+ base = type;
+ }
+ else if (type.equals(FIXEDBITMAP)) {
+ base = type;
+ }
+ else if (type.equals(OUTPUTSTRING) ||
+ type.equals(INPUTSTRING) ||
+ type.equals(STRINGVARIABLE)) {
+
+ base += ": \"" + getValue() + "\"";
+ }
+ else if (type.equals(NUMBERVARIABLE)) {
+ base += ": " + getValue();
+ }
+ else if (type.startsWith(COMMAND)) {
+ base = type.substring(type.indexOf('_') + 1, type.length());
+ }
+ Integer x = getX();
+ Integer y = getY();
+ if (x == null || y == null) {
+ return base;
+ }
+ else {
+ return "(" + x + ", " + y + ") " + base;
+ }
+ }
+ /**
+ * This method returns the value of the "font_size" attribute.
+ */
+ public Dimension getFontSize() {
+ String s = actual.getAttribute(FONT_SIZE);
+ return s.isEmpty() ? BitmapFont.indexToDimension(0) : BitmapFont.nameToDimension(s);
+ }
+
+ /**
+ * This method returns the value of the "font_size" attribute.
+ */
+ public String getFontSizeString() {
+ String f = actual.getAttribute(FONT_SIZE);
+ return f.isEmpty() ? BitmapFont.indexToName(0) : f;
+ }
+
+ public int getJustification(){
+ if (isJustificationMiddle()) {
+ return BitmapFont.JUSTIFICATION_MIDDLE;
+ }
+ else if (isJustificationRight()) {
+ return BitmapFont.JUSTIFICATION_RIGHT;
+ }
+ else {
+ return BitmapFont.JUSTIFICATION_LEFT;
+ }
+ }
+
+ public boolean isJustificationMiddle() {
+ String s = actual.getAttribute(HORIZONTAL_JUSTIFICATION);
+ return (s.equals("middle") || s.equals("1"));
+ }
+
+ public boolean isJustificationRight() {
+ String s = actual.getAttribute(HORIZONTAL_JUSTIFICATION);
+ return (s.equals("right") || s.equals("2"));
+ }
+
+ /**
+ * This method returns a integer representing the line supression of a rectangle
+ */
+ public int getLineSuppression() {
+ String s = actual.getAttribute(LINE_SUPPRESSION);
+ if (Integer.getInteger(s) != null) {
+ return Integer.parseInt(s);
+ }
+ int value = 0;
+ if (s.contains("top")) value += 1;
+ if (s.contains("right")) value += 2;
+ if (s.contains("bottom")) value += 4;
+ if (s.contains("left")) value += 8;
+
+ return value;
+ }
+
+ /**
+ * This method returns the value of the "fill_colour" attribute.
+ */
+ public Color getFillColor(int colorDepth) {
+ String f = actual.getAttribute(FILL_COLOUR);
+ return f.isEmpty() ? Color.GRAY : ColorPalette.getColor(f, colorDepth);
+ }
+ /**
+ * This method returns the value of the "colour" attribute.
+ */
+ public Color getColor(int colorDepth) {
+ String c = actual.getAttribute(COLOUR);
+ return c.isEmpty() ? Color.GRAY : ColorPalette.getColor(c, colorDepth);
+ }
+ /**
+ * This method returns the value of the "font_colour" attribute.
+ */
+ public Color getFontColor(int colorDepth) {
+ String c = actual.getAttribute(FONT_COLOUR);
+ return c.isEmpty() ? Color.GRAY : ColorPalette.getColor(c, colorDepth);
+ }
+ /**
+ * This method returns the value of the "background_colour" attribute.
+ */
+ public Color getBackgroundColor(int colorDepth) {
+ String c = actual.getAttribute(BACKGROUND_COLOUR);
+ return c.isEmpty() ? Color.GRAY : ColorPalette.getColor(c, colorDepth);
+ }
+ /**
+ * This method returns the value of the "line_colour" attribute.
+ */
+ public Color getLineColor(int colorDepth) {
+ String c = actual.getAttribute(LINE_COLOUR);
+ return c.isEmpty() ? Color.GRAY : ColorPalette.getColor(c, colorDepth);
+ }
+ /**
+ * This method returns the value of the "transparency_colour" attribute.
+ */
+ public Color getTransparencyColor(boolean reduceImages, int colorDepth) {
+ String c = actual.getAttribute(TRANSPARENCY_COLOUR);
+ return c.isEmpty() ? Color.GRAY : ColorPalette.getColor(c, reduceImages ? colorDepth : ColorPalette.COLOR_8BIT);
+ }
+
+ public BasicStroke getLineStroke() {
+ float w = Float.parseFloat(actual.getAttribute(LINE_WIDTH));
+ String lineArt = actual.getAttribute(LINE_ART);
+
+ // parse lineart
+ float[] dash = new float[32];
+ for (int i = 0; i < 16; i++) {
+ if (lineArt.charAt(i) == '0') {
+ dash[2 * i] = 0.0f;
+ dash[2 * i + 1] = 1.0f * (float) w;
+ }
+ else {
+ dash[2 * i] = 1.0f * (float) w;
+ dash[2 * i + 1] = 0.0f;
+ }
+ }
+ // the default attributes are a solid line of width 1.0, CAP_SQUARE,
+ // JOIN_MITER, a miter limit of 10.0.
+ //return new BasicStroke(w, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f);
+
+ //if CAP_SQUARE is used dashing might not be visible at all!!
+ return new BasicStroke(w, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f);
+ }
+
+ /**
+ * This method returns the value of the "fill_type" attribute.
+ */
+ public String getFillType() {
+ String f = actual.getAttribute(FILL_TYPE);
+ return f.isEmpty() ? "nofill" : f;
+ }
+
+ /**
+ * Opens the image
+ * Attributes are searched in this order: file8, file, file4, file1
+ */
+ public BufferedImage getImageFile() throws IOException {
+ XMLTreeNode root = (XMLTreeNode) model.getRoot();
+ return getImageFile(root.getStdBitmapPath());
+ }
+
+ private BufferedImage getImageFile(String imagepath) throws IOException {
+ BufferedImage image;
+ if ((image = getImageFile(FILE8, imagepath)) != null ||
+ (image = getImageFile(FILE, imagepath)) != null ||
+ (image = getImageFile(FILE4, imagepath)) != null ||
+ (image = getImageFile(FILE1, imagepath)) != null) {
+ return image;
+ }
+ return null;
+ }
+
+ private BufferedImage getImageFile(String fileAttr, String imagepath) throws IOException {
+ String s = actual.getAttribute(fileAttr);
+ File f = null;
+ if (s.isEmpty()) {
+ return null;
+ }
+ try {
+ //all \ are converted to / on a unix system and vice versa
+ f = new File((imagepath + s).replace('\\', File.separatorChar).replace('/', File.separatorChar));
+ return ImageIO.read(f);
+ }
+ catch (IOException ex) {
+ BufferedImage img = new BufferedImage(64, 40, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D gfx = (Graphics2D) img.getGraphics();
+ gfx.setColor(Color.RED);
+ gfx.fillRect(0, 0, 64, 40);
+ gfx.setColor(Color.YELLOW);
+ gfx.drawRect(0, 0, 63, 39);
+ gfx.drawString("MISSING", 5, 15);
+ gfx.drawString("IMAGE", 5, 35);
+ return img;
+ //throw new IOException(ex.getMessage() + " " + f);
+ }
+ }
+
+ /**
+ * This method returns the value of the "ellipse_type" attribute.
+ */
+ public Integer getEllipseType() {
+ String e = actual.getAttribute(ELLIPSE_TYPE);
+
+ // the cast (integer) is needed, otherwise auto-boxing does not work
+ return e.equals("open") ? (Integer) Arc2D.OPEN :
+ e.equals("closed") ? null :
+ e.equals("closedsegment") ? Arc2D.PIE :
+ e.equals("closedsection") ? Arc2D.CHORD :
+ /* default */ null;
+ }
+
+ public boolean isHidden() {
+ return isTrue(actual.getAttribute(HIDDEN));
+ }
+
+ public boolean isSelectable() {
+ return isTrue(actual.getAttribute(SELECTABLE));
+ }
+
+ static private boolean isTrue(String value) {
+ final String[] TRUE = new String[] {"yes", "true", "on", "show", "enable", "1"};
+ final String[] FALSE = new String[] {"no", "false", "off", "hide", "disable", "0"};
+ for (int i = 0, n = TRUE.length; i < n; i++) {
+ if (TRUE[i].equals(value)) {
+ return true;
+ }
+ }
+ for (int i = 0, n = FALSE.length; i < n; i++) {
+ if (FALSE[i].equals(value)) {
+ return false;
+ }
+ }
+ throw new RuntimeException("expecting a truth value (" + value + ")");
+ }
+
+ public boolean isPolygonTypeOpen() {
+ return actual.getAttribute(POLYGON_TYPE).equals("open");
+ }
+
+ //Outputmeter and linear bar graph methods
+ public boolean isOptionsArc() {
+ return actual.getAttribute(OPTIONS).contains("arc");
+ }
+
+ public boolean isOptionsBorder() {
+ return actual.getAttribute(OPTIONS).contains("border");
+ }
+
+ public boolean isOptionsTicks() {
+ return actual.getAttribute(OPTIONS).contains("ticks");
+ }
+ /**
+ * Returns whether of not no fill option is specified for Linear and
+ * arched bar graphs.
+ */
+ public boolean isOptionsNoFill() {
+ return actual.getAttribute(OPTIONS).contains("nofill");
+ }
+
+ public boolean isOptionsTargetLine() {
+ return actual.getAttribute(OPTIONS).contains("targetline");
+ }
+
+ public boolean isOptionsHorizontal() {
+ return actual.getAttribute(OPTIONS).contains("horizontal");
+ }
+
+ public void changeOptionsHorizontal(boolean horizontal) {
+ String options = actual.getAttribute(OPTIONS);
+
+ if(horizontal && !options.contains("horizontal")) {
+ options = options + "+horizontal";
+ actual.setAttribute(OPTIONS, options);
+ }
+
+ if(!horizontal && options.contains("horizontal")) {
+ options = options.replace("+horizontal", "");
+ options = options.replace("horizontal", "");
+ actual.setAttribute(OPTIONS, options);
+ }
+ }
+
+ public boolean isOptionsGrowPositive() {
+ return actual.getAttribute(OPTIONS).contains("growpositive");
+ }
+
+ public void changeOptionsGrowPositive(boolean growPositive) {
+ String options = actual.getAttribute(OPTIONS);
+
+ if(growPositive && !options.contains("growpositive")) {
+ options = options + "+growpositive";
+ actual.setAttribute(OPTIONS, options);
+ }
+
+ if(!growPositive && options.contains("growpositive")) {
+ options = options.replace("+growpositive", "");
+ options = options.replace("growpositive", "");
+ actual.setAttribute(OPTIONS, options);
+ }
+ }
+
+ public boolean isOptionsClockwise() {
+ return actual.getAttribute(OPTIONS).contains("clockwise");
+ }
+
+ public void changeOptionsClockwise(boolean clockwise) {
+ String options = actual.getAttribute(OPTIONS);
+
+ if (clockwise && !options.contains("clockwise")) {
+ options = options + "+clockwise";
+ actual.setAttribute(OPTIONS, options);
+ }
+
+ if (!clockwise && options.contains("clockwise")) {
+ options = options.replace("+clockwise", "");
+ options = options.replace("clockwise", "");
+ actual.setAttribute(OPTIONS, options);
+ }
+ }
+
+ public boolean isOptionsTransparent(){
+ return actual.getAttribute(OPTIONS).contains("transparent");
+ }
+
+ public boolean isOptionsFlashing() {
+ return actual.getAttribute(OPTIONS).contains("flashing");
+ }
+
+ public boolean isFontStyleFlashingHidden() {
+ return actual.getAttribute(FONT_STYLE).contains("flashinghidden");
+ }
+
+ public boolean isFontStyleFlashingInverted() {
+ return actual.getAttribute(FONT_STYLE).contains("flashinginverted");
+ }
+
+ public boolean isFontStyleInverted() {
+ String fontStyle = actual.getAttribute(FONT_STYLE);
+ StringTokenizer st = new StringTokenizer(fontStyle, "+");
+ while (st.hasMoreTokens()) {
+ if (st.nextToken().equals("inverted")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method returns the value of the "autowrap" bit in the "code" attribute.
+ */
+ public boolean isOptionsAutoWrap() {
+ return actual.getAttribute(OPTIONS).contains("autowrap");
+ }
+
+ /**
+ * This method returns the value of the "format" attribute.
+ */
+ public boolean isExponentialFormat() {
+ String f = actual.getAttribute(FORMAT);
+ return f.equals("exponential") || f.equals("1");
+ }
+
+ /**
+ * This method returns the value of the "leadingzeros" bit in the options
+ */
+ public boolean isOptionsDisplayLeadingZeros() {
+ return actual.getAttribute(OPTIONS).contains("leadingzeros");
+ }
+
+ /**
+ * This method returns the value of the "blankzero" bit in the options
+ */
+ public boolean isOptionsBlankZero() {
+ return actual.getAttribute(OPTIONS).contains("blankzero");
+ }
+
+ /**
+ * This method returns the value of the "needle_colour" attribute.
+ */
+ public Color getNeedleColor(int colorDepth) {
+ String n = actual.getAttribute(NEEDLE_COLOUR);
+ return n.isEmpty() ? Color.BLACK : ColorPalette.getColor(n, colorDepth);
+ }
+ /**
+ * This method returns the value of the "border_colour" attribute.
+ */
+ public Color getBorderColor(int colorDepth) {
+ String b = actual.getAttribute(BORDER_COLOUR);
+ return b.isEmpty() ? Color.BLACK : ColorPalette.getColor(b, colorDepth);
+ }
+ /**
+ * This method returns the value of the "target_line_colour" attribute.
+ */
+ public Color getTargetLineColor(int colorDepth) {
+ String t = actual.getAttribute(TARGET_LINE_COLOUR);
+ return t.isEmpty() ? Color.BLACK : ColorPalette.getColor(t, colorDepth);
+ }
+ /**
+ * This method returns the value of the "arc_and_tick_colour" attribute.
+ */
+ public Color getArcAndTickColor(int colorDepth) {
+ String a = actual.getAttribute(ARC_AND_TICK_COLOUR);
+ return a.isEmpty() ? Color.BLACK : ColorPalette.getColor(a, colorDepth);
+ }
+ /**
+ * This method returns the value of the "min_value" attribute.
+ */
+ public Integer getMinValue() {
+ String w = actual.getAttribute(MIN_VALUE);
+ return w.isEmpty() ? null : parseInt(w);
+ }
+ /**
+ * This method returns the value of the "max_value" attribute.
+ */
+ public Integer getMaxValue() {
+ String w = actual.getAttribute(MAX_VALUE);
+ return w.isEmpty() ? null : parseInt(w);
+ }
+ /**
+ * This method returns the value of the "number_of_ticks" attribute.
+ */
+ public Integer getTicks() {
+ String w = actual.getAttribute(NUMBER_OF_TICKS);
+ return w.isEmpty() ? null : parseInt(w);
+ }
+ /**
+ * This method returns the value of the "bar_graph_width" attribute.
+ */
+ public int getBargraphWidth() {
+ String w = actual.getAttribute(BAR_GRAPH_WIDTH);
+ return w.isEmpty() ? 0 : parseInt(w);
+ }
+ /**
+ * This method returns the value of the "scale" attribute.
+ */
+ public double getScale() {
+ String w = actual.getAttribute(SCALE);
+ return w.isEmpty() ? 1.0 : Double.parseDouble(w);
+ }
+ /**
+ * This method returns the value of the "offset" attribute.
+ */
+ public int getOffset(){
+ String w = actual.getAttribute(OFFSET);
+ return w.isEmpty() ? 0 : parseInt(w);
+ }
+ /**
+ * This method returns the value of the "value" attribute.
+ */
+ public String getValue() {
+ return actual.getAttribute(VALUE);
+ }
+ /**
+ * This method returns the value of the "value" attribute parsed as
+ * integer.
+ */
+ public Integer getValueInt() {
+ String v = actual.getAttribute(VALUE);
+ return v.isEmpty() ? null : parseInt(v);
+ }
+
+ /**
+ * This method returns the value of the "target_value" attribute.
+ */
+ public Integer getTargetValueInt() {
+ String v = actual.getAttribute(TARGET_VALUE);
+ return v.isEmpty() ? null : parseInt(v);
+ }
+ /**
+ * This method returns the value of the "code" attribute.
+ */
+ public String getCode() {
+ return actual.getAttribute(CODE);
+ }
+ /**
+ * This method returns the value of the "variable_reference" attribute.
+ */
+ public String getVariableReferenceName() {
+ String n = actual.getAttribute(VARIABLE_REFERENCE);
+ return n.isEmpty() ? null : n;
+ }
+ /**
+ * This method returns the value of the "target_value_variable_reference" attribute.
+ */
+ public String getTargetValueVariableReferenceName() {
+ String n = actual.getAttribute(TARGET_VALUE_VARIABLE_REFERENCE);
+ return n.isEmpty() ? null : n;
+ }
+ /**
+ * Returns a Paint-object which can be used for painting
+ */
+ public Paint getFillPaint(Color lineColor, String imagePath, Paint errPaint, boolean reduceImages, int colorDepth) throws IOException {
+ String fillType = this.getFillType();
+ if (fillType.equals("fillcolour")) {
+ return this.getFillColor(colorDepth);
+ }
+ else if (fillType.equals("linecolour")) {
+ return lineColor;
+ }
+ else if (fillType.equals("pattern")) {
+ BufferedImage image = null;
+ for (int j = 0, m = model.getChildCount( this ); j < m; j++) {
+ XMLTreeNode nd2 = (XMLTreeNode) model.getChild(this, j);
+ if (nd2.isType(PICTUREGRAPHIC)){
+ image = nd2.getImageFile();
+ Color transparencyColor = nd2.getTransparencyColor(reduceImages, colorDepth);
+ image = PictureConverter.applyTransparencyAndReduceColors(image,
+ transparencyColor, nd2.isOptionsTransparent(),
+ reduceImages, colorDepth);
+ }
+ }
+ return image == null ? errPaint :
+ new TexturePaint(image, new Rectangle2D.Double(0, 0, image.getWidth(), image.getHeight()));
+ }
+ // nofill
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the value of the "value" attribute from the number variable child
+ */
+ public Integer getNumberVariableValue(String role) {
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(NUMBERVARIABLE) && nd.getRole().equals(role)) {
+ return parseInt(nd.getValue());
+ }
+ }
+ return null;
+ }
+ /**
+ * Gets the value of the "value" attribute from the string variable child
+ */
+ public String getStringVariableValue() {
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(STRINGVARIABLE)) {
+ return nd.getValue();
+ }
+ }
+ return null;
+ }
+
+ public XMLTreeNode getFontAttributes() {
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(FONTATTRIBUTES)) {
+ return nd;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the value of the "font_colour" attribute from the font attributes child
+ * (for input boolean field)
+ public Color getFontAttributesColor(int colorDepth) {
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(FONTATTRIBUTES)) {
+ return nd.getFontColor(colorDepth);
+ }
+ }
+ return null;
+ }
+ */
+
+ public BitmapFont getFont(int colorDepth) {
+ return new BitmapFont(getFontSizeString(), getFontColor(colorDepth));
+ }
+
+ /**
+ * Gets the font attributes from the font attributes child
+ *
+ public BitmapFont getFontAttributesFont(int colorDepth) {
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(FONTATTRIBUTES)) {
+ return new BitmapFont(nd.getFontSizeString(), nd.getFontColor(colorDepth));
+ }
+ }
+ return null;
+ }
+ */
+ /**
+ * Gets the line attributes from the line attributes child
+ */
+ public LineAttributes getLineAttributes(int colorDepth) {
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(LINEATTRIBUTES)) {
+ return new LineAttributes(nd.getLineColor(colorDepth), nd.getLineStroke());
+ }
+ }
+ return null;
+ }
+ /**
+ * Gets the fill attributes from the fill attributes child
+ */
+ public Paint getFillAttributesPaint(Color lineColor, String imagepath, Paint errPaint, boolean reduceImages, int colorDepth) throws IOException {
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(FILLATTRIBUTES)) {
+ return nd.getFillPaint(lineColor, imagepath, errPaint, reduceImages, colorDepth);
+ }
+ }
+ return null;
+ }
+ /**
+ * Gets the polygon formed from the point children
+ */
+ public Polygon getPolygon() {
+ Polygon p = new Polygon();
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(POINT)) {
+ p.addPoint(nd.getX(), nd.getY());
+ }
+ }
+ return p;
+ }
+ /**
+ * Returns an array that has all points of polygon (x,y)-pairs
+ */
+ public int[] getPolygonPoints() {
+ // calculate the number of points
+ int pointsN = 0;
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(POINT)) {
+ pointsN++;
+ }
+ }
+ int[] points = new int[2*pointsN];
+ int index = 0;
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(POINT)) {
+ points[2 * index] = nd.getX();
+ points[2 * index + 1] = nd.getY();
+ index++;
+ }
+ }
+ return points;
+ }
+
+ /**
+ * Sets the wanted point of a polygon
+ */
+ public void setPolygonPoint(int index, int x, int y) {
+ int index2 = 0;
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(this, i);
+ if (nd.isType(POINT)) {
+ if (index2 == index) {
+ nd.setX(x);
+ nd.setY(y);
+ }
+ index2++;
+ }
+ }
+ }
+
+ /**
+ * There is a bug in Integer.parseInt - it does not handle "positive"
+ * strings e.g. "+123"
+ */
+ static public int parseInt(String value) {
+ if (value.startsWith("+")) {
+ value = value.substring(1);
+ }
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * Formats number (without leading zeros)
+ */
+ public String getFormatedNumber() {
+ int number_of_decimals = getNumberOfDecimals();
+ String format = "0";
+ if (isExponentialFormat()) {
+ // exponential format mantissa is allways 1-digit long (not zero)
+ if (number_of_decimals > 0) {
+ format = "0.";
+ }
+ for (int i = 0; i < number_of_decimals; i++) {
+ format += "0";
+ }
+ format += "E0";
+ }
+ else {
+ // fixed format
+ if (number_of_decimals > 0) {
+ format = "0.";
+ }
+ for (int i = 0; i < number_of_decimals; i++) {
+ format += "0";
+ }
+ }
+ DecimalFormat df = new DecimalFormat(format);
+ return df.format((getEffectiveValue() + getOffset()) * getScale());
+ }
+
+ /**
+ * Returns the value of the variable reference, if defined, otherwise
+ * returns the embedded value.
+ */
+ public Integer getEffectiveValue() {
+ Integer value = getNumberVariableValue(VARIABLE_REFERENCE); //getVariableReferenceName());
+ if (value == null) {
+ value = getValueInt();
+ }
+ return value;
+ }
+
+ /**
+ * Returns the value of the target variable reference, if defined, otherwise
+ * returns the embedded target value.
+ */
+ public Integer getEffectiveTargetValue() {
+ Integer targetLineValue = getNumberVariableValue(TARGET_VALUE_VARIABLE_REFERENCE);
+ if (targetLineValue == null) { // && isOptionsTargetLine()) {
+ targetLineValue = getTargetValueInt();
+ }
+ return targetLineValue;
+ }
+
+ /**
+ * Returns the size of this node. For example the size of a container
+ * is obtained by calling getWidth() and getHeight() methods, but the
+ * size of a meter is its width times width. The sizes of picture graphic
+ * and object pointer objects are more difficult to calculate.
+ */
+ public Dimension getNodeSize(Dimension dim) {
+ if (isType(WORKINGSET, KEY, AUXILIARYFUNCTION, AUXILIARYINPUT)) {
+ XMLTreeNode root = (XMLTreeNode) model.getRoot();
+ dim.setSize(root.getSKWidth(), root.getSKHeight());
+ }
+ else if (isType(DATAMASK, ALARMMASK)) {
+ XMLTreeNode root = (XMLTreeNode) model.getRoot();
+ int d = root.getDimension();
+ dim.setSize(d, d);
+ }
+ else if (isType(SOFTKEYMASK)) {
+ XMLTreeNode root = (XMLTreeNode) model.getRoot();
+ dim.setSize(root.getSKWidth(), model.getChildCount(actual) * root.getSKHeight());
+ }
+ else if (isType(CONTAINER, BUTTON, INPUTSTRING, INPUTNUMBER, INPUTLIST,
+ OUTPUTSTRING, OUTPUTNUMBER, LINE, RECTANGLE, ELLIPSE, POLYGON,
+ LINEARBARGRAPH, ARCHEDBARGRAPH)) {
+ dim.setSize(getWidth(), getHeight());
+ }
+ else if (isType(INPUTBOOLEAN, METER)) {
+ int w = getWidth();
+ dim.setSize(w, w);
+ }
+ else if (isType(PICTUREGRAPHIC)) {
+ try {
+ BufferedImage image = getImageFile(); // for size calculation only
+ if(image != null) {
+ int w = getWidth();
+ int h = w * image.getHeight() / image.getWidth();
+ dim.setSize(w, h);
+ }
+ }
+ catch (IOException ex) {
+ ex.printStackTrace();
+ dim.setSize(64, 64); // questimate picture size?
+ }
+ }
+ else if (isType(OBJECTPOINTER)) {
+ // should be only one
+ for (int i = 0, n = model.getChildCount(this); i < n; i++) {
+ XMLTreeNode nd = (XMLTreeNode) model.getChild(actual, i);
+ nd.getNodeSize(dim);
+ }
+ }
+ else if (isType(POINT)) {
+ dim.setSize(1, 1);
+ }
+ else {
+ dim.setSize(0, 0); // object has no size?
+ }
+ return dim;
+ }
+}
diff --git a/src/treemodel/XMLTreeNodeList.java b/src/treemodel/XMLTreeNodeList.java
new file mode 100644
index 0000000..104943e
--- /dev/null
+++ b/src/treemodel/XMLTreeNodeList.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package treemodel;
+
+import static pooledit.Definitions.*;
+import java.util.ArrayList;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Element;
+import pooledit.Utils;
+
+/**
+ *
+ * @author mohman
+ */
+class XMLTreeNodeList extends ArrayList {
+
+ private final XMLTreeNode parent;
+
+ /**
+ * Constructor.
+ * @param list
+ */
+ protected XMLTreeNodeList(XMLTreeNodeList list) {
+ super(list);
+ this.parent = list.parent;
+ }
+
+ /**
+ * Constructor.
+ * @param parent
+ */
+ protected XMLTreeNodeList(XMLTreeNode parent) {
+ this.parent = parent;
+
+ XMLTreeModel model = parent.getModel();
+ String type = parent.getType();
+ int index;
+
+ // parent is root -> create category nodes
+ if ((index = Utils.indexEquals(type, OBJECTPOOL)) >= 0) {
+ for (int i = 0, n = CATEGORIES.length; i < n; i++) {
+ XMLTreeNode node = model.CATEGORY_NODES[i];
+ add(node);
+ }
+ }
+ // parent is a category node -> create subcategory nodes
+ else if ((index = Utils.indexEquals(type, CATEGORIES)) >= 0) {
+ for (int i = 0, n = SUBCATEGORYGROUPS[index].length; i < n; i++) {
+ XMLTreeNode node = model.SUBCATEGORY_NODES[index][i];
+ add(node);
+ }
+ }
+ // parent is a subcategory node -> create actual nodes stored in subcategories
+ else if ((index = Utils.indexEquals(type, SUBCATEGORIES)) >= 0) {
+
+ // iterate over all elements
+ Element root = parent.getModel().getDocument().getDocumentElement();
+ NodeList elements = root.getChildNodes();
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Node node = elements.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE &&
+ node.getNodeName().equals(OBJECTS[index])) {
+
+ add(parent.createChildNode((Element) node));
+ }
+ }
+ }
+ // parent is a real node (actual or link)
+ else {
+
+ // add child elements
+ Element element = parent.actual();
+ NodeList elements = element.getChildNodes();
+ for (int i = 0, n = elements.getLength(); i < n; i++) {
+ Node node = elements.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ add(parent.createChildNode((Element) node));
+ }
+ }
+ }
+ }
+}
diff --git a/src/wizard/LineTrendGenerator.java b/src/wizard/LineTrendGenerator.java
new file mode 100644
index 0000000..6540ec6
--- /dev/null
+++ b/src/wizard/LineTrendGenerator.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package wizard;
+
+import static wizard.WizardTools.*;
+import static pooledit.Definitions.*;
+import font.BitmapFont;
+import java.awt.Dimension;
+import org.w3c.dom.Element;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class LineTrendGenerator {
+
+ private LineTrendWizard wiz;
+ private XMLTreeNode root;
+ private String name;
+
+ /**
+ * Creates a new instance of TrendGenerator
+ */
+ public LineTrendGenerator(LineTrendWizard wiz, XMLTreeNode root, String name) {
+ this.wiz = wiz;
+ this.root = root;
+ this.name = name;
+ wiz.setTrendGenerator(this);
+ }
+
+ public XMLTreeNode getRoot() {
+ return root;
+ }
+
+ /**
+ * This method is called when the table should be updated
+ */
+ public void update() {
+ int width = wiz.getContainerWidth();
+ int height = wiz.getContainerHeight();
+ Element container = createContainer(root.actual(), name, width, height);
+
+ if (root.isType(OBJECTS)) {
+ setAttributeIfMissing(container, POS_X, "0");
+ setAttributeIfMissing(container, POS_Y, "0");
+ }
+
+ if (wiz.isHorizontal()) {
+ updateHorizontal(container, width, height);
+ }
+ else {
+ updateVertical(container, width, height);
+ }
+ }
+
+ private void updateVertical(Element container, int width, int height) {
+ boolean growPos = !wiz.isGrowsLeftDown();
+ int nroNumbers = wiz.getNumbers();
+ int max = wiz.getMaxValue();
+ int min = wiz.getMinValue();
+ String maxValue = Integer.toString(max);
+ String minValue = Integer.toString(min);
+
+ String fontName = wiz.getFontAttribute();
+ Element font = root.getModel().getElementByName(fontName);
+ Dimension fontdim = BitmapFont.nameToDimension(font.getAttribute(FONT_SIZE));
+ int xPad = fontdim.width / 2 + 1;
+ int yPad = fontdim.height / 2 + 1;
+ int nroWidth = 0;
+ int nroHeight = 0;
+ if (wiz.isDrawNumbers()) {
+ nroWidth = fontdim.width * maxValue.length();
+ }
+ if (wiz.isDrawTitle()) {
+ nroHeight = fontdim.height;
+ }
+
+ int nroBars = wiz.getNroBars();
+ int barWidth = (width - nroWidth - 2 * xPad) / nroBars;
+ int barHeight = height - nroHeight - 2 * yPad;
+
+ Element bg = createRectangle(container, "background", width, height,
+ wiz.getLineAttribute1(), wiz.getFillAttribute1(), false);
+ setIncludeAttributes(bg, 0, 0);
+
+ if (wiz.isDrawNumbers()) {
+ if (nroNumbers == 1) {
+ int nro = (min + max) / 2;
+ int posY = barHeight / 2;
+ Element label = createString(container, "label0", nroWidth,
+ fontdim.height, Integer.toString(nro), fontName);
+ setIncludeAttributes(label, 0, posY);
+ removeExtraElements(container, "label", 1);
+ }
+ else if (nroNumbers > 1) {
+ int nroInc = (max - min) / (nroNumbers - 1);
+ int posYInc = barHeight / (nroNumbers - 1);
+ for (int i = 0, nro = min, posY = 1; i < nroNumbers;
+ i++, nro += nroInc, posY += posYInc) {
+ Element label = createString(container, "label" + i, nroWidth,
+ fontdim.height, Integer.toString(growPos ? max - nro : nro), fontName);
+ setIncludeAttributes(label, 2, posY);
+ }
+ removeExtraElements(container, "label", nroNumbers);
+ }
+ else {
+ removeExtraElements(container, "label", 0);
+ }
+ }
+ else {
+ removeExtraElements(container, "label", 0);
+ }
+
+ if (wiz.isDrawTitle()) {
+ String title = wiz.getTitle();
+ int titleWidth = fontdim.width * title.length();
+ Element tle = createString(container, "title0", titleWidth,
+ fontdim.height, title, fontName);
+ setIncludeAttributes(tle, (width - titleWidth) / 2, height - nroHeight - 2);
+ }
+ else {
+ removeExtraElements(container, "title", 0);
+ }
+
+ Element panel = createRectangle(container, "panel", nroBars * barWidth + 2, barHeight + 2,
+ wiz.getLineAttribute2(), wiz.getFillAttribute2(), false);
+ setIncludeAttributes(panel, nroWidth + xPad - 1, yPad - 1);
+
+ for (int i = 0, posX = nroWidth + xPad; i < nroBars; i++, posX += barWidth) {
+ Element line = createLine(container, "line" + i, barWidth + 1,
+ barHeight, (i % 2 == 0), wiz.getLineAttribute3());
+ setIncludeAttributes(line, posX, yPad);
+ }
+ removeExtraElements(container, "line", nroBars);
+ }
+
+ private void updateHorizontal(Element container, int width, int height) {
+ // FIXME: not implemented!
+ }
+
+ private Element createLine(Element father, String name,
+ int width, int height, boolean blToRt, String lineAttribute) {
+
+ Element line = createElement(LINE, name, father, false);
+ setAttribute(line, WIDTH, width);
+ setAttribute(line, HEIGHT, height);
+ setAttribute(line, LINE_DIRECTION,
+ blToRt ? "bottomlefttotopright" : "toplefttobottomright");
+ createIncludeRoleElement(lineAttribute, LINE_ATTRIBUTES, line);
+
+ return line;
+ }
+
+ private static void setOptions(Element elem, String[] names, boolean[] values) {
+ String oldOpt = elem.getAttribute(OPTIONS);
+ String newOpt = oldOpt;
+
+ for (int i = 0; i < names.length; i++) {
+ // setting attribute
+ if (values[i]) {
+ if (!newOpt.contains(names[i])) {
+ newOpt = newOpt.concat("+" + names[i]);
+ }
+ }
+ // resetting attribute
+ else {
+ newOpt = newOpt.replace(names[i], "");
+ }
+ }
+ // trim attribute
+ newOpt.replace("++", "+");
+ if (newOpt.startsWith("+")) {
+ newOpt = newOpt.substring(1);
+ }
+ if (newOpt.endsWith("+")) {
+ newOpt = newOpt.substring(0, newOpt.length() - 1);
+ }
+ if (!newOpt.equals(oldOpt)) {
+ elem.setAttribute(OPTIONS, newOpt);
+ }
+ }
+}
diff --git a/src/wizard/LineTrendWizard.form b/src/wizard/LineTrendWizard.form
new file mode 100644
index 0000000..f68fc56
--- /dev/null
+++ b/src/wizard/LineTrendWizard.form
@@ -0,0 +1,436 @@
+
+
+
diff --git a/src/wizard/LineTrendWizard.java b/src/wizard/LineTrendWizard.java
new file mode 100644
index 0000000..d638895
--- /dev/null
+++ b/src/wizard/LineTrendWizard.java
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package wizard;
+
+import static wizard.WizardTools.*;
+import static pooledit.Definitions.*;
+import attributetable.AttributeTable;
+import color.ColorPalette;
+import javax.swing.JFrame;
+import javax.swing.ListCellRenderer;
+import javax.swing.DefaultComboBoxModel;
+import org.w3c.dom.Element;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class LineTrendWizard extends javax.swing.JPanel {
+
+ private LineTrendGenerator lineTrendGenerator;
+
+ private final String[] colors = ColorPalette.getAllColorNames();
+ private DefaultComboBoxModel colorModel = new DefaultComboBoxModel(colors);
+ private ListCellRenderer colorRenderer = AttributeTable.createColorListRenderer();
+ private DefaultComboBoxModel fillModel1;
+ private DefaultComboBoxModel lineModel1;
+ private DefaultComboBoxModel fillModel2;
+ private DefaultComboBoxModel lineModel2;
+ private DefaultComboBoxModel lineModel3;
+
+ private DefaultComboBoxModel fontModel;
+
+
+ /**
+ * Creates new form BeanForm
+ */
+ public LineTrendWizard() {
+ // initComponents();
+ }
+
+ public void setTrendGenerator(LineTrendGenerator lineTrendGenerator) {
+ this.lineTrendGenerator = lineTrendGenerator;
+ Element root = lineTrendGenerator.getRoot().getModel().getDocument().getDocumentElement();
+ fillModel1 = new DefaultComboBoxModel(WizardTools.findElementsWithEmpty(root, FILLATTRIBUTES));
+ lineModel1 = new DefaultComboBoxModel(WizardTools.findElements(root, LINEATTRIBUTES));
+ fillModel2 = new DefaultComboBoxModel(WizardTools.findElementsWithEmpty(root, FILLATTRIBUTES));
+ lineModel2 = new DefaultComboBoxModel(WizardTools.findElements(root, LINEATTRIBUTES));
+ lineModel3 = new DefaultComboBoxModel(WizardTools.findElements(root, LINEATTRIBUTES));
+
+ fontModel = new DefaultComboBoxModel(WizardTools.findElements(root, FONTATTRIBUTES));
+ initComponents();
+ }
+
+ /** This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+ jLabel1 = new javax.swing.JLabel();
+ jLabel2 = new javax.swing.JLabel();
+ jWidthSpinner = new javax.swing.JSpinner();
+ jHeightSpinner = new javax.swing.JSpinner();
+ jLabel3 = new javax.swing.JLabel();
+ jLabel4 = new javax.swing.JLabel();
+ jMinValueSpinner = new javax.swing.JSpinner();
+ jMaxValueSpinner = new javax.swing.JSpinner();
+ jHorizontalCB = new javax.swing.JCheckBox();
+ jGrowsLeftDownCB = new javax.swing.JCheckBox();
+ jSeparator1 = new javax.swing.JSeparator();
+ jLabel6 = new javax.swing.JLabel();
+ jLabel7 = new javax.swing.JLabel();
+ jSeparator2 = new javax.swing.JSeparator();
+ jLabel8 = new javax.swing.JLabel();
+ jFillAttribute2CB = new javax.swing.JComboBox();
+ jNroBarsSpinner = new javax.swing.JSpinner();
+ jLabel9 = new javax.swing.JLabel();
+ jLineAttribute2CB = new javax.swing.JComboBox();
+ jLabel10 = new javax.swing.JLabel();
+ jFillAttribute1CB = new javax.swing.JComboBox();
+ jLabel11 = new javax.swing.JLabel();
+ jLineAttribute1CB = new javax.swing.JComboBox();
+ jLabel12 = new javax.swing.JLabel();
+ jTitleTF = new javax.swing.JTextField();
+ jLabel13 = new javax.swing.JLabel();
+ jFontAttribCB = new javax.swing.JComboBox();
+ jShowTitleCB = new javax.swing.JCheckBox();
+ jLabel14 = new javax.swing.JLabel();
+ jNumbersSpinner = new javax.swing.JSpinner();
+ jDrawNumbersCB = new javax.swing.JCheckBox();
+ jRefreshButton = new javax.swing.JButton();
+ jOKButton = new javax.swing.JButton();
+ jLineAttribute3CB = new javax.swing.JComboBox();
+
+ jLabel1.setText("Width");
+
+ jLabel2.setText("Height");
+
+ jWidthSpinner.setValue(100);
+ jWidthSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jWidthSpinnerStateChanged(evt);
+ }
+ });
+
+ jHeightSpinner.setValue(100);
+ jHeightSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jHeightSpinnerStateChanged(evt);
+ }
+ });
+
+ jLabel3.setText("Min Value");
+
+ jLabel4.setText("Max Value");
+
+ jMinValueSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jMinValueSpinnerStateChanged(evt);
+ }
+ });
+
+ jMaxValueSpinner.setValue(100);
+ jMaxValueSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jMaxValueSpinnerStateChanged(evt);
+ }
+ });
+
+ jHorizontalCB.setText("Horizontal");
+ jHorizontalCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jHorizontalCB.setEnabled(false);
+ jHorizontalCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jGrowsLeftDownCB.setText("Grows Left / Down");
+ jGrowsLeftDownCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jGrowsLeftDownCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jLabel6.setText("Nro Bars");
+
+ jLabel7.setText("Line attr, line");
+
+ jLabel8.setText("Fill attr, frame");
+
+ jFillAttribute2CB.setModel(fillModel2);
+
+ jNroBarsSpinner.setValue(6);
+ jNroBarsSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jNroBarsSpinnerStateChanged(evt);
+ }
+ });
+
+ jLabel9.setText("Line attr, frame");
+
+ jLineAttribute2CB.setModel(lineModel2);
+
+ jLabel10.setText("Fill Attribute");
+
+ jFillAttribute1CB.setModel(fillModel1);
+
+ jLabel11.setText("Line Attribute");
+
+ jLineAttribute1CB.setModel(lineModel1);
+
+ jLabel12.setText("Title");
+
+ jTitleTF.setText("trend");
+
+ jLabel13.setText("Font Attribute");
+
+ jFontAttribCB.setModel(fontModel);
+
+ jShowTitleCB.setSelected(true);
+ jShowTitleCB.setText("Show Title");
+ jShowTitleCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jShowTitleCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jLabel14.setText("Numbers");
+
+ jNumbersSpinner.setValue(6);
+ jNumbersSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jNumbersSpinnerStateChanged(evt);
+ }
+ });
+
+ jDrawNumbersCB.setSelected(true);
+ jDrawNumbersCB.setText("Draw Numbers");
+ jDrawNumbersCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jDrawNumbersCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jRefreshButton.setText("Refresh");
+ jRefreshButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jRefreshButtonActionPerformed(evt);
+ }
+ });
+
+ jOKButton.setText("OK");
+ jOKButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jOKButtonActionPerformed(evt);
+ }
+ });
+
+ jLineAttribute3CB.setModel(lineModel3);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel1)
+ .addComponent(jLabel2))
+ .addGap(26, 26, 26)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(jHeightSpinner)
+ .addComponent(jWidthSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 48, Short.MAX_VALUE))
+ .addGap(16, 16, 16)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jGrowsLeftDownCB)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel11)
+ .addComponent(jLabel10))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jFillAttribute1CB, javax.swing.GroupLayout.Alignment.TRAILING, 0, 110, Short.MAX_VALUE)
+ .addComponent(jLineAttribute1CB, 0, 110, Short.MAX_VALUE)))))
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel6))
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel3))
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel4))
+ .addComponent(jLabel14))
+ .addGap(8, 8, 8)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jNumbersSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 46, Short.MAX_VALUE)
+ .addComponent(jMaxValueSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 46, Short.MAX_VALUE)
+ .addComponent(jMinValueSpinner, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 46, Short.MAX_VALUE)
+ .addComponent(jNroBarsSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 46, Short.MAX_VALUE))
+ .addGap(14, 14, 14)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jDrawNumbersCB)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel7)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 25, Short.MAX_VALUE)
+ .addComponent(jLineAttribute3CB, 0, 101, Short.MAX_VALUE))
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel8)
+ .addComponent(jLabel9))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jFillAttribute2CB, javax.swing.GroupLayout.Alignment.TRAILING, 0, 101, Short.MAX_VALUE)
+ .addComponent(jLineAttribute2CB, 0, 101, Short.MAX_VALUE)))))
+ .addComponent(jHorizontalCB)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jShowTitleCB)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel12)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jTitleTF, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(16, 16, 16)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel13)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jFontAttribCB, 0, 102, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addComponent(jOKButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jRefreshButton)))))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED))
+ .addComponent(jSeparator2, javax.swing.GroupLayout.DEFAULT_SIZE, 339, Short.MAX_VALUE)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 339, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(jWidthSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jFillAttribute1CB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel10))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel2)
+ .addComponent(jHeightSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel11)
+ .addComponent(jLineAttribute1CB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jHorizontalCB)
+ .addComponent(jGrowsLeftDownCB))
+ .addGap(15, 15, 15)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel6)
+ .addComponent(jNroBarsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel7)
+ .addComponent(jLineAttribute3CB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel3)
+ .addComponent(jMinValueSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel8)
+ .addComponent(jFillAttribute2CB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel4)
+ .addComponent(jMaxValueSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel9)
+ .addComponent(jLineAttribute2CB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(32, 32, 32)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel14)
+ .addComponent(jNumbersSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jDrawNumbersCB))
+ .addGap(14, 14, 14)
+ .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 11, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel12)
+ .addComponent(jTitleTF, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel13)
+ .addComponent(jFontAttribCB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jShowTitleCB)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 7, Short.MAX_VALUE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jOKButton)
+ .addComponent(jRefreshButton))
+ .addContainerGap())
+ );
+ }// //GEN-END:initComponents
+
+ private void jHeightSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jHeightSpinnerStateChanged
+ checkSpinnerLimits(jHeightSpinner, 0, 65535);
+ }//GEN-LAST:event_jHeightSpinnerStateChanged
+
+ private void jWidthSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jWidthSpinnerStateChanged
+ checkSpinnerLimits(jWidthSpinner, 0, 65535);
+ }//GEN-LAST:event_jWidthSpinnerStateChanged
+
+ private void jNumbersSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jNumbersSpinnerStateChanged
+ checkSpinnerLimits(jNumbersSpinner, 0, 99); // 10 might be too little
+ }//GEN-LAST:event_jNumbersSpinnerStateChanged
+
+ private void jMaxValueSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jMaxValueSpinnerStateChanged
+ checkSpinnerLimits(jMaxValueSpinner, 0, 65535);
+ }//GEN-LAST:event_jMaxValueSpinnerStateChanged
+
+ private void jMinValueSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jMinValueSpinnerStateChanged
+ checkSpinnerLimits(jMinValueSpinner, 0, 65535);
+ }//GEN-LAST:event_jMinValueSpinnerStateChanged
+
+ private void jNroBarsSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jNroBarsSpinnerStateChanged
+ checkSpinnerLimits(jNroBarsSpinner, 0, 99);
+ }//GEN-LAST:event_jNroBarsSpinnerStateChanged
+
+ private void jRefreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jRefreshButtonActionPerformed
+ if (lineTrendGenerator != null) {
+ lineTrendGenerator.update();
+ }
+ }//GEN-LAST:event_jRefreshButtonActionPerformed
+
+ private void jOKButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jOKButtonActionPerformed
+ if (lineTrendGenerator != null) {
+ lineTrendGenerator.update();
+ }
+ ((JFrame) this.getParent().getParent().getParent().getParent()).setVisible(false);
+ }//GEN-LAST:event_jOKButtonActionPerformed
+
+ public boolean isDrawNumbers() {
+ return jDrawNumbersCB.isSelected();
+ }
+ public String getFillAttribute1() {
+ return (String) jFillAttribute1CB.getSelectedItem();
+ }
+ public String getFillAttribute2() {
+ return (String) jFillAttribute2CB.getSelectedItem();
+ }
+ public String getFontAttribute() {
+ return (String) jFontAttribCB.getSelectedItem();
+ }
+ public boolean isGrowsLeftDown() {
+ return jGrowsLeftDownCB.isSelected();
+ }
+ public int getContainerHeight() {
+ return (Integer) jHeightSpinner.getValue();
+ }
+ public boolean isHorizontal() {
+ return jHorizontalCB.isSelected();
+ }
+ public String getLineAttribute1() {
+ return (String) jLineAttribute1CB.getSelectedItem();
+ }
+ public String getLineAttribute2() {
+ return (String) jLineAttribute2CB.getSelectedItem();
+ }
+ public String getLineAttribute3() {
+ return (String) jLineAttribute3CB.getSelectedItem();
+ }
+ public int getMaxValue() {
+ return (Integer) jMaxValueSpinner.getValue();
+ }
+ public int getMinValue() {
+ return (Integer) jMinValueSpinner.getValue();
+ }
+ public int getNroBars() {
+ return (Integer) jNroBarsSpinner.getValue();
+ }
+ public int getNumbers() {
+ return (Integer) jNumbersSpinner.getValue();
+ }
+ public boolean isDrawTitle() {
+ return jShowTitleCB.isSelected();
+ }
+ public String getTitle() {
+ return jTitleTF.getText();
+ }
+ public int getContainerWidth() {
+ return (Integer) jWidthSpinner.getValue();
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JCheckBox jDrawNumbersCB;
+ private javax.swing.JComboBox jFillAttribute1CB;
+ private javax.swing.JComboBox jFillAttribute2CB;
+ private javax.swing.JComboBox jFontAttribCB;
+ private javax.swing.JCheckBox jGrowsLeftDownCB;
+ private javax.swing.JSpinner jHeightSpinner;
+ private javax.swing.JCheckBox jHorizontalCB;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel10;
+ private javax.swing.JLabel jLabel11;
+ private javax.swing.JLabel jLabel12;
+ private javax.swing.JLabel jLabel13;
+ private javax.swing.JLabel jLabel14;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel3;
+ private javax.swing.JLabel jLabel4;
+ private javax.swing.JLabel jLabel6;
+ private javax.swing.JLabel jLabel7;
+ private javax.swing.JLabel jLabel8;
+ private javax.swing.JLabel jLabel9;
+ private javax.swing.JComboBox jLineAttribute1CB;
+ private javax.swing.JComboBox jLineAttribute2CB;
+ private javax.swing.JComboBox jLineAttribute3CB;
+ private javax.swing.JSpinner jMaxValueSpinner;
+ private javax.swing.JSpinner jMinValueSpinner;
+ private javax.swing.JSpinner jNroBarsSpinner;
+ private javax.swing.JSpinner jNumbersSpinner;
+ private javax.swing.JButton jOKButton;
+ private javax.swing.JButton jRefreshButton;
+ private javax.swing.JSeparator jSeparator1;
+ private javax.swing.JSeparator jSeparator2;
+ private javax.swing.JCheckBox jShowTitleCB;
+ private javax.swing.JTextField jTitleTF;
+ private javax.swing.JSpinner jWidthSpinner;
+ // End of variables declaration//GEN-END:variables
+
+}
diff --git a/src/wizard/MeterGenerator.java b/src/wizard/MeterGenerator.java
new file mode 100644
index 0000000..f60cb5c
--- /dev/null
+++ b/src/wizard/MeterGenerator.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package wizard;
+
+import static pooledit.Definitions.*;
+import static wizard.WizardTools.*;
+import font.BitmapFont;
+import org.w3c.dom.Element;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author jkalmari
+ */
+public class MeterGenerator {
+
+ private MeterWizard wiz;
+ private XMLTreeNode root;
+ private String name;
+
+ //A list of elements that are created in process
+ //private List elements = new ArrayList();
+
+ /**
+ * Creates a new instance of MeterGenerator
+ * father is the father-elment where the meter should be added
+ */
+ public MeterGenerator(MeterWizard meterWizard, XMLTreeNode root, String name) {
+ this.wiz = meterWizard;
+ this.root = root;
+ this.name = name;
+ meterWizard.setMeterGenerator(this);
+ }
+
+ public XMLTreeNode getRoot() {
+ return root;
+ }
+
+ /**
+ * This method is called when the meter should be updated
+ */
+ public void update() {
+ // create container
+ Element container = createContainer(root.actual(), name,
+ wiz.getTotalWidth(), wiz.getTotalHeight());
+
+ if (root.isType(OBJECTS)) {
+ setAttributeIfMissing(container, POS_X, "0");
+ setAttributeIfMissing(container, POS_Y, "0");
+ }
+
+ // create background circle
+ int startAng = wiz.getStartAngle();
+ int endAng = wiz.getEndAngle();
+ Element ellipse;
+ ellipse = createEllipse(container, "background", wiz.getBackgroundWidth(), wiz.getBackgroundWidth(),
+ startAng, endAng,
+ wiz.getFillAttribute(), wiz.getLineAttribute(), !wiz.isCuttedCircle());
+ setIncludeAttributes(ellipse, (wiz.getTotalWidth() - wiz.getBackgroundWidth()) / 2,
+ (wiz.getTotalHeight() - wiz.getBackgroundWidth()) / 2);
+
+ //create meter
+ int minVal = (int) (wiz.getMinValue() / wiz.getScale()) - wiz.getOffset();
+ int maxVal = (int) (wiz.getMaxValue() / wiz.getScale()) - wiz.getOffset();
+ Element meter = createMeter(container, "metercomp", wiz.getMeterWidth(),
+ startAng, endAng, minVal, maxVal,
+ wiz.getValue(), wiz.getTicks(), wiz.isClockwise(),
+ wiz.getNeedleColor(), wiz.getArcAndTickColor(), wiz.getNumberReference());
+ setIncludeAttributes(meter, (wiz.getTotalWidth() - wiz.getMeterWidth()) / 2,
+ (wiz.getTotalHeight() - wiz.getMeterWidth()) / 2);
+
+ // create numbers
+ if(startAng > endAng) startAng -= 360;
+ if( wiz.isClockwise() ) {
+ int temp = startAng;
+ startAng = endAng;
+ endAng = temp;
+ }
+ Element fontElement = root.getModel().getElementByName(wiz.getFontAttribute());
+ int fontWidth = (int) BitmapFont.nameToDimension(fontElement.getAttribute(FONT_SIZE)).getWidth();
+ int fontHeight = (int) BitmapFont.nameToDimension(fontElement.getAttribute(FONT_SIZE)).getHeight();
+ int i;
+ for (i = 0; i < wiz.getNumbers() && wiz.getNumbers() > 1; i++) {
+ int value = wiz.getMinValue() + i*(wiz.getMaxValue()-wiz.getMinValue()) / (wiz.getNumbers()-1);
+ String valueString = Integer.toString(value);
+ int stringWidth = (fontWidth * valueString.length());
+
+ Element number = createString(container, "label" + i, fontWidth * valueString.length(),
+ fontHeight, valueString, wiz.getFontAttribute());
+
+ double ang = Math.toRadians(startAng + i * (endAng - startAng) / (wiz.getNumbers() - 1));
+ double rad = wiz.getMeterWidth() / 2 + wiz.getNumberDistance();
+ int x = wiz.getTotalWidth() / 2 + (int) (Math.cos(-ang) * rad) - stringWidth / 2;
+ int y = wiz.getTotalHeight() / 2 + (int) (Math.sin(-ang) * rad) - fontHeight / 2;
+ setIncludeAttributes(number, x, y);
+ }
+
+ // labelX, X >= i -> poistetaan
+ removeExtraElements(container, "label", i);
+
+ // create a numberfield
+ int extraSpace = wiz.getOffset() < 0 ? 1 : 0;
+ int nroWidth = fontWidth * (Integer.toString(wiz.getMaxValue()).length() + extraSpace);
+ Element numberfield = createNumber(container, "number", nroWidth, fontHeight, wiz.getValue(),
+ wiz.getNumberReference(), wiz.getFontAttribute(), wiz.getOffset(), wiz.getScale());
+ setIncludeAttributes(numberfield, (wiz.getTotalWidth() - nroWidth/*meterWizard.getMeterWidth()*/) / 2,
+ wiz.getTotalHeight() / 2 + wiz.getMeterWidth() / 4);
+
+ // create heading
+ String text = wiz.getHeading();
+ Element heading = createString(container, "title", fontWidth*text.length(), fontHeight, text, wiz.getFontAttribute());
+ setIncludeAttributes(heading, (wiz.getTotalWidth() - fontWidth * text.length()) / 2,
+ wiz.getTotalHeight() / 2 - wiz.getMeterWidth() / 4);
+
+ // moves the attribute-objects to include objects with roles
+ // System.out.println("finalizing...");
+ // Tools.createRoles(container);
+ }
+
+ /**
+ * Creates a new element to document and adds it as a child
+ * if name is null a new name is created
+ * if father is null, element will be added to root
+ private static Element createElement(String type, String name, Element father) {
+
+ // iterate ovet father's direct children to see if it already has
+ // the specified child
+ NodeList children = father.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ if (elem.getNodeName() == type &&
+ elem.getAttribute(NAME).equals(name)) {
+ return elem;
+ }
+ }
+ }
+
+ // if new element has to be created, we do it this way to the default
+ // attributes "free" from the schema
+ Document tmp = Tools.parseDocument("<" + type + "/>",
+ "C:\\pooledit\\schema\\iso11783.xsd");
+ Element elem = (Element) father.getOwnerDocument().adoptNode(tmp.getDocumentElement()).getFirstChild();
+ elem.setAttribute(NAME, name);
+ father.appendChild(elem);
+ return elem;
+ }
+ */
+
+ /*
+ private static Element createContainer(Element father, String name, int width, int height) {
+ Element container = createElement(CONTAINER, name, father);
+ setAttribute(container, WIDTH, width);
+ setAttribute(container, HEIGHT, height);
+ return container;
+ }
+ */
+ private static Element createEllipse(Element father, String name, int width, int height,
+ int startAngle, int endAngle, String fillAttribute, String lineAttribute, boolean closed) {
+
+ Element ellipse = createElement(ELLIPSE, name, father, false);
+ setAttribute(ellipse, WIDTH, width);
+ setAttribute(ellipse, HEIGHT, height);
+ setAttribute(ellipse, START_ANGLE, startAngle);
+ setAttribute(ellipse, END_ANGLE, endAngle);
+ setAttribute(ellipse, ELLIPSE_TYPE, closed ? "closed" : "closedsection");
+ //setAttribute(ellipse, FILL_ATTRIBUTES, fillAttribute);
+ //setAttribute(ellipse, LINE_ATTRIBUTES, lineAttribute);
+
+ createIncludeRoleElement(fillAttribute, FILL_ATTRIBUTES, ellipse);
+ createIncludeRoleElement(lineAttribute, LINE_ATTRIBUTES, ellipse);
+ return ellipse;
+ }
+
+ private static Element createMeter(Element father, String name, int width, int startAngle,
+ int endAngle, int minValue, int maxValue, int value, int ticks, boolean clockwise,
+ String needleColor, String arcAndTickColor, String variableReference) {
+
+ Element meter = createElement(METER, name, father, false);
+ setAttribute(meter, WIDTH, width);
+ //setAttribute(meter, HEIGHT, height); // METER DOES NOT HAVE SEPARATE HEIGHT FIELD!
+ setAttribute(meter, START_ANGLE, startAngle);
+ setAttribute(meter, END_ANGLE, endAngle);
+ setAttribute(meter, MIN_VALUE, minValue);
+ setAttribute(meter, MAX_VALUE, maxValue);
+ setAttribute(meter, VALUE, value);
+ setAttribute(meter, NUMBER_OF_TICKS, ticks);
+ setAttribute(meter, OPTIONS,"ticks+arc" + (clockwise ? "+clockwise" : ""));
+ setAttribute(meter, NEEDLE_COLOUR, needleColor);
+ setAttribute(meter, ARC_AND_TICK_COLOUR, arcAndTickColor);
+ //setAttribute(meter, VARIABLE_REFERENCE, variableReference);
+
+ createIncludeRoleElement(variableReference, VARIABLE_REFERENCE, meter);
+ return meter;
+ }
+
+ private static Element createNumber(Element father, String name, int width, int height, int value,
+ String variableReference, String fontAttribute, int offset, double scale) {
+
+ Element number = createElement(OUTPUTNUMBER, name, father, false);
+ setAttribute(number, WIDTH, width);
+ setAttribute(number, HEIGHT, height);
+ setAttribute(number, VALUE, value);
+ setAttribute(number, HORIZONTAL_JUSTIFICATION, "middle");
+ setAttribute(number, OPTIONS, "transparent");
+ setAttribute(number, SCALE, scale);
+ setAttribute(number, OFFSET, offset);
+ //setAttribute(number, NUMBER_OF_DECIMALS, "0");
+ setAttribute(number, BACKGROUND_COLOUR, "white");
+
+ //setAttribute(number, FONT_ATTRIBUTES, fontAttribute);
+ //setAttribute(number, VARIABLE_REFERENCE, variableReference);
+
+ createIncludeRoleElement(fontAttribute, FONT_ATTRIBUTES, number);
+ createIncludeRoleElement(variableReference, VARIABLE_REFERENCE, number);
+ return number;
+ }
+}
diff --git a/src/wizard/MeterWizard.form b/src/wizard/MeterWizard.form
new file mode 100644
index 0000000..16988dd
--- /dev/null
+++ b/src/wizard/MeterWizard.form
@@ -0,0 +1,624 @@
+
+
+
diff --git a/src/wizard/MeterWizard.java b/src/wizard/MeterWizard.java
new file mode 100644
index 0000000..9c8c38a
--- /dev/null
+++ b/src/wizard/MeterWizard.java
@@ -0,0 +1,711 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package wizard;
+
+import static wizard.WizardTools.*;
+import static pooledit.Definitions.*;
+import attributetable.AttributeTable;
+import color.ColorPalette;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JFrame;
+import javax.swing.ListCellRenderer;
+import javax.swing.SpinnerNumberModel;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author jkalmari
+ */
+public class MeterWizard extends javax.swing.JPanel {
+ //this is called when model has to be updated
+ private MeterGenerator meterGenerator;
+
+ private String[] colors = ColorPalette.getAllColorNames();
+ private DefaultComboBoxModel colorModel1 = new DefaultComboBoxModel(colors);
+ private DefaultComboBoxModel colorModel2 = new DefaultComboBoxModel(colors);
+ private ListCellRenderer colorRenderer = AttributeTable.createColorListRenderer();
+ private DefaultComboBoxModel fillModel;
+ private DefaultComboBoxModel fontModel;
+ private DefaultComboBoxModel lineModel;
+ private DefaultComboBoxModel numberModel;
+
+ /** Creates new form MeterWizard */
+ public MeterWizard() {
+ //initComponents();
+ }
+
+ public void setMeterGenerator(MeterGenerator meterGenerator) {
+ this.meterGenerator = meterGenerator;
+ Element root = meterGenerator.getRoot().getModel().getDocument().getDocumentElement();
+ fillModel = new DefaultComboBoxModel(WizardTools.findElementsWithEmpty(root, FILLATTRIBUTES));
+ fontModel = new DefaultComboBoxModel(WizardTools.findElements(root, FONTATTRIBUTES));
+ lineModel = new DefaultComboBoxModel(WizardTools.findElements(root, LINEATTRIBUTES));
+ numberModel = new DefaultComboBoxModel(WizardTools.findElementsWithEmpty(root, NUMBERVARIABLE));
+ initComponents();
+ }
+
+ /** This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+ jLabel1 = new javax.swing.JLabel();
+ jLabel2 = new javax.swing.JLabel();
+ jLabel3 = new javax.swing.JLabel();
+ jLabel4 = new javax.swing.JLabel();
+ jSpinner1 = new javax.swing.JSpinner();
+ jSpinner2 = new javax.swing.JSpinner();
+ jSpinner3 = new javax.swing.JSpinner();
+ jSpinner4 = new javax.swing.JSpinner();
+ jLabel5 = new javax.swing.JLabel();
+ jSpinner5 = new javax.swing.JSpinner();
+ jLabel6 = new javax.swing.JLabel();
+ jCheckBox1 = new javax.swing.JCheckBox();
+ jSeparator1 = new javax.swing.JSeparator();
+ jLabel7 = new javax.swing.JLabel();
+ jSpinner6 = new javax.swing.JSpinner();
+ jLabel8 = new javax.swing.JLabel();
+ jSpinner7 = new javax.swing.JSpinner();
+ jLabel9 = new javax.swing.JLabel();
+ jLabel10 = new javax.swing.JLabel();
+ jLabel12 = new javax.swing.JLabel();
+ jSpinner8 = new javax.swing.JSpinner();
+ jSpinner9 = new javax.swing.JSpinner();
+ jSpinner11 = new javax.swing.JSpinner();
+ jSeparator2 = new javax.swing.JSeparator();
+ jLabel13 = new javax.swing.JLabel();
+ jLabel15 = new javax.swing.JLabel();
+ jLabel16 = new javax.swing.JLabel();
+ jButton1 = new javax.swing.JButton();
+ jLabel18 = new javax.swing.JLabel();
+ jSpinner12 = new javax.swing.JSpinner();
+ jComboBox1 = new javax.swing.JComboBox();
+ jComboBox2 = new javax.swing.JComboBox();
+ jLabel19 = new javax.swing.JLabel();
+ jLabel20 = new javax.swing.JLabel();
+ jLabel21 = new javax.swing.JLabel();
+ jComboBox3 = new javax.swing.JComboBox();
+ jComboBox4 = new javax.swing.JComboBox();
+ jComboBox5 = new javax.swing.JComboBox();
+ jComboBox6 = new javax.swing.JComboBox();
+ jButton4 = new javax.swing.JButton();
+ jSeparator3 = new javax.swing.JSeparator();
+ jSeparator4 = new javax.swing.JSeparator();
+ jLabel11 = new javax.swing.JLabel();
+ jLabel14 = new javax.swing.JLabel();
+ jSpinner13 = new javax.swing.JSpinner();
+ jTextField1 = new javax.swing.JTextField();
+ jLabel17 = new javax.swing.JLabel();
+ jCheckBox2 = new javax.swing.JCheckBox();
+ jTextField2 = new javax.swing.JTextField();
+ jLabel22 = new javax.swing.JLabel();
+
+ jLabel1.setText("Start angle");
+
+ jLabel2.setText("End angle");
+
+ jLabel3.setText("Min value");
+
+ jLabel4.setText("Max value");
+
+ jSpinner1.setValue(320);
+ jSpinner1.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner1StateChanged(evt);
+ }
+ });
+
+ jSpinner2.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner2StateChanged(evt);
+ }
+ });
+
+ jSpinner3.setValue(220);
+ jSpinner3.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner3StateChanged(evt);
+ }
+ });
+
+ jSpinner4.setValue(100);
+ jSpinner4.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner4StateChanged(evt);
+ }
+ });
+
+ jLabel5.setText("Value");
+
+ jSpinner5.setValue(5);
+ jSpinner5.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner5StateChanged(evt);
+ }
+ });
+
+ jLabel6.setText("Clockwise");
+
+ jCheckBox1.setSelected(true);
+ jCheckBox1.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jCheckBox1.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jLabel7.setText("Ticks");
+
+ jSpinner6.setValue(11);
+ jSpinner6.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner6StateChanged(evt);
+ }
+ });
+
+ jLabel8.setText("Numbers");
+
+ jSpinner7.setValue(6);
+ jSpinner7.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner7StateChanged(evt);
+ }
+ });
+
+ jLabel9.setText("Container size");
+
+ jLabel10.setText("Background size");
+
+ jLabel12.setText("Meter size");
+
+ jSpinner8.setValue(100);
+ jSpinner8.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner8StateChanged(evt);
+ }
+ });
+
+ jSpinner9.setValue(100);
+ jSpinner9.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner9StateChanged(evt);
+ }
+ });
+
+ jSpinner11.setValue(70);
+ jSpinner11.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner11StateChanged(evt);
+ }
+ });
+
+ jLabel13.setText("Needle color");
+
+ jLabel15.setText("Fill attribute");
+
+ jLabel16.setText("Arc and tick color");
+
+ jButton1.setText("Refresh");
+ jButton1.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButton1ActionPerformed(evt);
+ }
+ });
+
+ jLabel18.setText("Numbers distance");
+
+ jSpinner12.setValue(7);
+ jSpinner12.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner12StateChanged(evt);
+ }
+ });
+
+ jComboBox1.setModel(colorModel1);
+ jComboBox1.setRenderer(colorRenderer);
+
+ jComboBox2.setModel(colorModel2);
+ jComboBox2.setRenderer(colorRenderer);
+
+ jLabel19.setText("Font attribute");
+
+ jLabel20.setText("Line attribute");
+
+ jLabel21.setText("Number reference");
+
+ jComboBox3.setModel(fillModel);
+
+ jComboBox4.setModel(fontModel);
+
+ jComboBox5.setModel(lineModel);
+
+ jComboBox6.setModel(numberModel);
+
+ jButton4.setText("OK");
+ jButton4.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButton4ActionPerformed(evt);
+ }
+ });
+
+ jLabel11.setText("Scale");
+
+ jLabel14.setText("Offset");
+
+ jSpinner13.setValue(0);
+ jSpinner13.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jSpinner13StateChanged(evt);
+ }
+ });
+
+ jTextField1.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
+ jTextField1.setText("1.0");
+ jTextField1.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ jTextField1FocusLost(evt);
+ }
+ });
+
+ jLabel17.setText("Cut circle");
+
+ jCheckBox2.setSelected(true);
+ jCheckBox2.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jCheckBox2.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jTextField2.setText("km/h");
+
+ jLabel22.setText("Heading");
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(19, 19, 19)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jSpinner2, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jSpinner5, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addGap(52, 52, 52)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(33, 33, 33)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel4, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jCheckBox1)
+ .addComponent(jSpinner4, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jSpinner3, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addGroup(layout.createSequentialGroup()
+ .addGap(35, 35, 35)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel8, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jSpinner7, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel12, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jSpinner11, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(jLabel22, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jLabel18, javax.swing.GroupLayout.PREFERRED_SIZE, 70, Short.MAX_VALUE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jSpinner12, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)))))))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel7, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(18, 18, 18)
+ .addComponent(jSpinner6, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel9, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jSpinner8, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addComponent(jLabel10, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 18, Short.MAX_VALUE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jCheckBox2)
+ .addComponent(jSpinner9, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)))))
+ .addContainerGap(45, Short.MAX_VALUE))
+ .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, 438, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jSeparator4, javax.swing.GroupLayout.DEFAULT_SIZE, 450, Short.MAX_VALUE)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 450, Short.MAX_VALUE)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jLabel11, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(15, 15, 15)
+ .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(87, 87, 87)
+ .addComponent(jLabel14, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jSpinner13, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(102, Short.MAX_VALUE))
+ .addComponent(jSeparator2, javax.swing.GroupLayout.DEFAULT_SIZE, 450, Short.MAX_VALUE)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jLabel17, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(370, Short.MAX_VALUE))
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel13, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel19, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel15, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(18, 18, 18)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(jComboBox1, 0, 111, Short.MAX_VALUE)
+ .addComponent(jComboBox4, javax.swing.GroupLayout.PREFERRED_SIZE, 111, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jComboBox3, javax.swing.GroupLayout.PREFERRED_SIZE, 111, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(19, 19, 19)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jLabel16, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel20, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel21, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jComboBox2, javax.swing.GroupLayout.PREFERRED_SIZE, 103, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jComboBox5, javax.swing.GroupLayout.PREFERRED_SIZE, 103, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jComboBox6, javax.swing.GroupLayout.PREFERRED_SIZE, 103, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jButton4)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jButton1)))
+ .addContainerGap(45, Short.MAX_VALUE))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel2)
+ .addComponent(jSpinner3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel3)
+ .addComponent(jSpinner2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel4)
+ .addComponent(jSpinner4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel5)
+ .addComponent(jSpinner5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel6))
+ .addComponent(jCheckBox1)))
+ .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel7)
+ .addComponent(jSpinner6, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel8)
+ .addComponent(jSpinner7, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel9)
+ .addComponent(jSpinner8, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel10)
+ .addComponent(jSpinner9, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel12)
+ .addComponent(jSpinner11, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel18)
+ .addComponent(jSpinner12, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jLabel17)
+ .addComponent(jCheckBox2)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel22)))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel11)
+ .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel14)
+ .addComponent(jSpinner13, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jSeparator4, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel13)
+ .addComponent(jLabel16)
+ .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jComboBox2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel15)
+ .addComponent(jLabel20)
+ .addComponent(jComboBox5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jComboBox3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel19)
+ .addComponent(jLabel21)
+ .addComponent(jComboBox4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jComboBox6, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(16, 16, 16)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jButton4)
+ .addComponent(jButton1))
+ .addContainerGap())
+ );
+ }// //GEN-END:initComponents
+
+ private void jTextField1FocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jTextField1FocusLost
+ System.out.println("Text changed");
+ String text = jTextField1.getText();
+ double value = 1.0;
+ try {
+ value = Double.valueOf(text);
+ } catch (NumberFormatException ex) {
+ value = 1.0f;
+ }
+ jTextField1.setText(Double.toString(value));
+ }//GEN-LAST:event_jTextField1FocusLost
+
+ private void jSpinner13StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner13StateChanged
+ // offset
+ }//GEN-LAST:event_jSpinner13StateChanged
+
+ private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed
+ // OK
+ if (meterGenerator != null) {
+ meterGenerator.update();
+ ((JFrame) this.getParent().getParent().getParent().getParent()).setVisible(false); //FIXME is this best way to close the window?
+ }
+ }//GEN-LAST:event_jButton4ActionPerformed
+
+ private void jSpinner12StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner12StateChanged
+ // number distance
+ }//GEN-LAST:event_jSpinner12StateChanged
+
+ private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
+ // Refresh
+ if (meterGenerator != null) {
+ meterGenerator.update();
+ }
+ }//GEN-LAST:event_jButton1ActionPerformed
+
+ private void jSpinner11StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner11StateChanged
+ checkSpinnerLimits(jSpinner11, 0, 65535);
+ }//GEN-LAST:event_jSpinner11StateChanged
+
+ private void jSpinner9StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner9StateChanged
+ checkSpinnerLimits(jSpinner9, 0, 65535);
+ }//GEN-LAST:event_jSpinner9StateChanged
+
+ private void jSpinner8StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner8StateChanged
+ checkSpinnerLimits(jSpinner8, 0, 65535);
+ }//GEN-LAST:event_jSpinner8StateChanged
+
+ private void jSpinner7StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner7StateChanged
+ checkSpinnerLimits(jSpinner7, 0, 255);
+ }//GEN-LAST:event_jSpinner7StateChanged
+
+ private void jSpinner6StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner6StateChanged
+ checkSpinnerLimits(jSpinner6, 0, 255);
+ }//GEN-LAST:event_jSpinner6StateChanged
+
+ private void jSpinner5StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner5StateChanged
+ checkSpinnerLimits(jSpinner5, 0, 65535);
+ }//GEN-LAST:event_jSpinner5StateChanged
+
+ private void jSpinner4StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner4StateChanged
+ checkSpinnerLimits(jSpinner4, 0, 65535);
+ }//GEN-LAST:event_jSpinner4StateChanged
+
+ private void jSpinner2StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner2StateChanged
+ checkSpinnerLimits(jSpinner2, 0, 65535);
+ }//GEN-LAST:event_jSpinner2StateChanged
+
+ private void jSpinner3StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner3StateChanged
+ checkSpinnerLimits(jSpinner3, 0, 360);
+ }//GEN-LAST:event_jSpinner3StateChanged
+
+ private void jSpinner1StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSpinner1StateChanged
+ checkSpinnerLimits(jSpinner1, 0, 360);
+ }//GEN-LAST:event_jSpinner1StateChanged
+
+ public int getStartAngle() {
+ return (Integer) ((SpinnerNumberModel) jSpinner1.getModel()).getValue();
+ }
+ public int getEndAngle() {
+ return (Integer) ((SpinnerNumberModel) jSpinner3.getModel()).getValue();
+ }
+ public int getMinValue() {
+ return (Integer) ((SpinnerNumberModel) jSpinner2.getModel()).getValue();
+ }
+ public int getMaxValue() {
+ return (Integer) ((SpinnerNumberModel) jSpinner4.getModel()).getValue();
+ }
+ public int getValue() {
+ return (Integer) ((SpinnerNumberModel) jSpinner5.getModel()).getValue();
+ }
+ public int getTicks() {
+ return (Integer) ((SpinnerNumberModel) jSpinner6.getModel()).getValue();
+ }
+ public int getNumbers() {
+ return (Integer) ((SpinnerNumberModel) jSpinner7.getModel()).getValue();
+ }
+ public int getTotalWidth() {
+ return (Integer) ((SpinnerNumberModel) jSpinner8.getModel()).getValue();
+ }
+ public int getTotalHeight() {
+ return (Integer) ((SpinnerNumberModel) jSpinner8.getModel()).getValue();
+ }
+ public int getBackgroundWidth() {
+ return (Integer) ((SpinnerNumberModel) jSpinner9.getModel()).getValue();
+ }
+ public int getMeterWidth() {
+ return (Integer) ((SpinnerNumberModel) jSpinner11.getModel()).getValue();
+ }
+ public int getNumberDistance() {
+ return (Integer) ((SpinnerNumberModel) jSpinner12.getModel()).getValue();
+ }
+ public boolean isClockwise() {
+ return jCheckBox1.getModel().isSelected();
+ }
+ public boolean isCuttedCircle() {
+ return jCheckBox2.getModel().isSelected();
+ }
+ public String getNeedleColor(){
+ return (String) colorModel1.getSelectedItem();
+ }
+ public String getArcAndTickColor(){
+ return (String) colorModel2.getSelectedItem();
+ }
+ public String getFillAttribute(){
+ return (String) jComboBox3.getModel().getSelectedItem();
+ }
+ public String getFontAttribute(){
+ return (String) jComboBox4.getModel().getSelectedItem();
+ }
+ public String getLineAttribute(){
+ return (String) jComboBox5.getModel().getSelectedItem();
+ }
+ public String getNumberReference(){
+ return (String) jComboBox6.getModel().getSelectedItem();
+ }
+ public String getHeading(){
+ return jTextField2.getText();
+ }
+ public int getOffset(){
+ return (Integer) ((SpinnerNumberModel) jSpinner13.getModel()).getValue();
+ }
+ public Double getScale() {
+ return Double.valueOf(jTextField1.getText());
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton jButton1;
+ private javax.swing.JButton jButton4;
+ private javax.swing.JCheckBox jCheckBox1;
+ private javax.swing.JCheckBox jCheckBox2;
+ private javax.swing.JComboBox jComboBox1;
+ private javax.swing.JComboBox jComboBox2;
+ private javax.swing.JComboBox jComboBox3;
+ private javax.swing.JComboBox jComboBox4;
+ private javax.swing.JComboBox jComboBox5;
+ private javax.swing.JComboBox jComboBox6;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel10;
+ private javax.swing.JLabel jLabel11;
+ private javax.swing.JLabel jLabel12;
+ private javax.swing.JLabel jLabel13;
+ private javax.swing.JLabel jLabel14;
+ private javax.swing.JLabel jLabel15;
+ private javax.swing.JLabel jLabel16;
+ private javax.swing.JLabel jLabel17;
+ private javax.swing.JLabel jLabel18;
+ private javax.swing.JLabel jLabel19;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel20;
+ private javax.swing.JLabel jLabel21;
+ private javax.swing.JLabel jLabel22;
+ private javax.swing.JLabel jLabel3;
+ private javax.swing.JLabel jLabel4;
+ private javax.swing.JLabel jLabel5;
+ private javax.swing.JLabel jLabel6;
+ private javax.swing.JLabel jLabel7;
+ private javax.swing.JLabel jLabel8;
+ private javax.swing.JLabel jLabel9;
+ private javax.swing.JSeparator jSeparator1;
+ private javax.swing.JSeparator jSeparator2;
+ private javax.swing.JSeparator jSeparator3;
+ private javax.swing.JSeparator jSeparator4;
+ private javax.swing.JSpinner jSpinner1;
+ private javax.swing.JSpinner jSpinner11;
+ private javax.swing.JSpinner jSpinner12;
+ private javax.swing.JSpinner jSpinner13;
+ private javax.swing.JSpinner jSpinner2;
+ private javax.swing.JSpinner jSpinner3;
+ private javax.swing.JSpinner jSpinner4;
+ private javax.swing.JSpinner jSpinner5;
+ private javax.swing.JSpinner jSpinner6;
+ private javax.swing.JSpinner jSpinner7;
+ private javax.swing.JSpinner jSpinner8;
+ private javax.swing.JSpinner jSpinner9;
+ private javax.swing.JTextField jTextField1;
+ private javax.swing.JTextField jTextField2;
+ // End of variables declaration//GEN-END:variables
+
+}
diff --git a/src/wizard/TableGenerator.java b/src/wizard/TableGenerator.java
new file mode 100644
index 0000000..e4fed1b
--- /dev/null
+++ b/src/wizard/TableGenerator.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package wizard;
+
+import static pooledit.Definitions.*;
+import static wizard.WizardTools.*;
+import font.BitmapFont;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import treemodel.XMLTreeNode;
+
+
+/**
+ *
+ * @author jkalmari
+ */
+public class TableGenerator {
+
+ static private final char SEPARATOR = '_';
+ private static final String CELLNAME = "cell";
+ private TableWizard wiz;
+ private XMLTreeNode root;
+ private String name;
+
+ /**
+ * Creates a new instance of TableGenerator
+ */
+ public TableGenerator(TableWizard tableWizard, XMLTreeNode root, String name) {
+ this.wiz = tableWizard;
+ this.root = root;
+ this.name = name;
+ tableWizard.setTableGenerator(this);
+ }
+
+ public XMLTreeNode getRoot() {
+ return root;
+ }
+
+ /**
+ * This method is called when the table should be updated
+ */
+ public void update() {
+ // create container
+ int width = wiz.getCellWidth() * wiz.getCellsHorizontal() + 1;
+ width += wiz.hasHeadingColumn() ? wiz.getHeadingColumnWidth() : 0;
+ int height = wiz.getCellHeight() * wiz.getCellsVertical() + 1;
+ height += wiz.hasHeadingRow() ? wiz.getHeadingRowHeight() : 0;
+
+ Element container = createContainer(root.actual(), name, width, height);
+
+ if (root.isType(OBJECTS)) {
+ setAttributeIfMissing(container, POS_X, "0");
+ setAttributeIfMissing(container, POS_Y, "0");
+ }
+
+ if (wiz.hasHeadingColumn()) {
+ Element rectangle = createRectangle(container, "colrectangle",
+ wiz.getHeadingColumnWidth()+1, height, wiz.getLineAttribute(),
+ wiz.getFillAttributeHeading(), true);
+ setIncludeAttributes(rectangle, 0, 0);
+ }
+ else {
+ removeObject(container, "colrectangle");
+ }
+
+ //create a box under the heading row
+ if(wiz.hasHeadingRow()) {
+ Element rectangle = createRectangle(container, "rowrectangle", width,
+ wiz.getHeadingRowHeight()+1, wiz.getLineAttribute(),
+ wiz.getFillAttributeHeading(), true);
+ setIncludeAttributes(rectangle, 0, 0);
+ }
+ else {
+ removeObject(container, "rowrectangle");
+ }
+
+ //create a box under the table
+ removeObject(container, "rectangle");
+ Element rectangle = createRectangle(container, "rectangle", width, height,
+ wiz.getLineAttribute(), wiz.getFillAttributeCell(), true);
+ setIncludeAttributes(rectangle, 0, 0);
+
+ //create horizontal lines
+ int i;
+ if (wiz.hasHeadingRow())
+ for (i = 0; i < wiz.getCellsVertical(); i++){
+ Element horizontalLine = createLine(container, "lineh"+i, width, 1, wiz.getLineAttribute());
+ setIncludeAttributes(horizontalLine, 0, i*wiz.getCellHeight() + wiz.getHeadingRowHeight());
+ }
+ else
+ for (i = 0; i < (wiz.getCellsVertical()-1); i++){
+ Element horizontalLine = createLine(container, "lineh"+i, width, 1, wiz.getLineAttribute());
+ setIncludeAttributes(horizontalLine, 0, (i+1)*wiz.getCellHeight());
+ }
+ removeExtraElements(container, "lineh", i);
+
+ //create vertical lines
+ if (wiz.hasHeadingColumn()) {
+ for (i = 0; i < wiz.getCellsHorizontal(); i++){
+ Element verticalLine = createLine(container, "linev"+i, 1, height, wiz.getLineAttribute());
+ setIncludeAttributes(verticalLine, i*wiz.getCellWidth() + wiz.getHeadingColumnWidth(), 0);
+ }
+ }
+ else {
+ for (i = 0; i < (wiz.getCellsHorizontal() - 1); i++){
+ Element verticalLine = createLine(container, "linev"+i, 1, height, wiz.getLineAttribute());
+ setIncludeAttributes(verticalLine, (i+1)*wiz.getCellWidth(), 0);
+ }
+ }
+ removeExtraElements(container, "linev", i);
+
+ //create text and number cells
+ Element fontElement = root.getModel().getElementByName( wiz.getFontAttribute());
+ int fontHeight = (int) BitmapFont.nameToDimension(fontElement.getAttribute(FONT_SIZE)).getHeight();
+
+ int maxRow = wiz.getCellsVertical() + (wiz.hasHeadingRow() ? 1 : 0);
+ int maxCol = wiz.getCellsHorizontal() + (wiz.hasHeadingColumn() ? 1 : 0);
+
+ //System.err.println("mRow: " + maxRow + ", mCol: " + maxCol);
+ for (int row = 0; row < maxRow; row++) {
+ for (int col = 0; col < maxCol; col++) {
+ String name = CELLNAME + row + SEPARATOR + col;
+
+ int x = ((wiz.hasHeadingColumn() && col > 0) ?
+ ((col - 1) * wiz.getCellWidth() + wiz.getHeadingColumnWidth()) :
+ col * wiz.getCellWidth()) + 1;
+ int y = ((wiz.hasHeadingRow() && row > 0) ?
+ ((row - 1) * wiz.getCellHeight() + wiz.getHeadingRowHeight()) :
+ row * wiz.getCellHeight()) + 1;
+
+ int cellWidth = ((wiz.hasHeadingColumn() && col == 0) ?
+ wiz.getHeadingColumnWidth() :
+ wiz.getCellWidth()) - 1;
+ int cellHeight = ((wiz.hasHeadingRow() && row == 0) ?
+ wiz.getHeadingRowHeight() :
+ wiz.getCellHeight()) - 1;
+
+ if (wiz.getVerticalJustification().equals("Middle")) {
+ y += (cellHeight - fontHeight)/2;
+ cellHeight -= (cellHeight - fontHeight)/2;
+ }
+ if (wiz.getVerticalJustification().equals("Bottom")) {
+ y += (cellHeight - fontHeight);
+ cellHeight -= (cellHeight - fontHeight);
+ }
+
+ //create a text cell
+ if ((row == 0 && wiz.hasHeadingRow()) || (col == 0 && wiz.hasHeadingColumn())) {
+ removeElement(container, OUTPUTNUMBER, name);
+ Element cell = createString(container, name, cellWidth, cellHeight, "A",
+ wiz.getFontAttribute(), wiz.getHorizontalJustification().toLowerCase());
+ setIncludeAttributes(cell, x, y);
+ }
+ else {
+ removeElement(container, OUTPUTSTRING, name);
+ Element cell = createNumber(container, name, cellWidth, cellHeight, 0, null,
+ wiz.getFontAttribute(), wiz.getHorizontalJustification().toLowerCase(),
+ wiz.getOffset(), wiz.getScale(), wiz.getNumberOfDecimals());
+ setIncludeAttributes(cell, x, y);
+ }
+ }
+ }
+ //remove old elements
+ removeExtraElements2(container, CELLNAME, maxRow, maxCol);
+
+ //Tools.createRoles(container);
+ }
+
+ private static Element createLine(Element father, String name,
+ int width, int height, String lineAttribute) {
+
+ Element line = createElement(LINE, name, father, false);
+ setAttribute(line, WIDTH, width);
+ setAttribute(line, HEIGHT, height);
+ setAttribute(line, LINE_DIRECTION, "toplefttobottomright");
+ createIncludeRoleElement(lineAttribute, LINE_ATTRIBUTES, line);
+ return line;
+ }
+
+ private static Element createNumber(Element father, String name,
+ int width, int height, int value,
+ String variableReference, String fontAttribute, String justification,
+ int offset, double scale, int decimals) {
+
+ Element number = createElement(OUTPUTNUMBER, name, father, false);
+ setAttribute(number, WIDTH, width);
+ setAttribute(number, HEIGHT, height);
+ setAttribute(number, VALUE, value);
+ setAttribute(number, HORIZONTAL_JUSTIFICATION, justification);
+ setAttribute(number, OPTIONS, "transparent");
+ setAttribute(number, SCALE, scale);
+ setAttribute(number, OFFSET, offset);
+ setAttribute(number, NUMBER_OF_DECIMALS, decimals);
+ setAttribute(number, BACKGROUND_COLOUR, "white");
+
+ createIncludeRoleElement(fontAttribute, FONT_ATTRIBUTES, number);
+ createIncludeRoleElement(variableReference, VARIABLE_REFERENCE, number);
+
+ return number;
+ }
+
+ /**
+ * Creates a new include object child element that points to an object
+ * called "name". Removes any other child elements that have the same role.
+ * If name is null, no new element is created and the method returns null.
+ private static Element createIncludeRoleElement(String name, String role, Element father) {
+ Element rv = null;
+ List worklist = new ArrayList();
+ NodeList children = father.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ if (elem.getAttribute(ROLE).equals(role)) {
+ if (elem.getAttribute(NAME).equals(name)) {
+ rv = elem;
+ }
+ else {
+ worklist.add(elem);
+ }
+ }
+ }
+ }
+ for (Element elem : worklist) {
+ father.removeChild(elem);
+ }
+
+ if (rv == null && name != null) {
+ // if new element has to be created, we do it this way to the default
+ // attributes "free" from the schema
+ Document tmp = Tools.parseDocument("",
+ "C:\\pooledit\\schema\\iso11783.xsd");
+ rv = (Element) father.getOwnerDocument().adoptNode(tmp.getDocumentElement()).getFirstChild();
+ rv.setAttribute(NAME, name);
+ rv.setAttribute(ROLE, role);
+ father.appendChild(rv);
+ }
+ return rv;
+ }
+ */
+
+ private static Element includeObject(Element father, Element child,
+ int posX, int posY) {
+
+ Element includeObject = createElement(INCLUDE_OBJECT, child.getAttribute(NAME), father, false);
+ includeObject.setAttribute(POS_X, Integer.toString(posX));
+ includeObject.setAttribute(POS_Y, Integer.toString(posY));
+ return includeObject;
+ }
+
+ private static void removeElement(Element father, String type, String name){
+ NodeList children = father.getChildNodes();
+ for (int i = children.getLength() - 1; i >= 0; i--) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ if (elem.getNodeName().equals(type) && elem.getAttribute(NAME).equals(name)) {
+ father.removeChild(elem);
+ }
+ }
+ }
+ }
+ /*
+ private static void removeExtraElements(Element father, String name, int start) {
+ NodeList children = father.getChildNodes();
+ for (int i = children.getLength() - 1; i >= 0; i--) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ String fullname = elem.getAttribute(NAME);
+ if (fullname.startsWith(name)) {
+ try {
+ String end = fullname.substring(name.length());
+ int nro = Integer.parseInt(end);
+ if (nro >= start) {
+ father.removeChild(elem);
+ }
+ }
+ catch (NumberFormatException e) {}
+ }
+ }
+ }
+ }
+ */
+ private static void removeExtraElements2(Element father, String name, int rowStart, int colStart) {
+ NodeList children = father.getChildNodes();
+ for (int i = children.getLength() - 1; i >= 0; i--) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ String fullname = elem.getAttribute(NAME);
+ if (fullname.startsWith(name)) {
+ try {
+ int separatorIndex = fullname.indexOf(SEPARATOR, name.length());
+ String end1 = fullname.substring(name.length(), separatorIndex);
+ String end2 = fullname.substring(separatorIndex + 1);
+ int row = Integer.parseInt(end1);
+ int col = Integer.parseInt(end2);
+ if (row >= rowStart || col >= colStart) {
+ father.removeChild(elem);
+ }
+ }
+ catch (NumberFormatException e) {}
+ }
+ }
+ }
+ }
+
+ private static void removeObject(Element father, String name) {
+ NodeList children = father.getChildNodes();
+ for (int i = children.getLength() - 1; i >= 0; i--) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ if (elem.getAttribute(NAME).equals(name)) {
+ father.removeChild(elem);
+ }
+ }
+ }
+ }
+}
diff --git a/src/wizard/TableWizard.form b/src/wizard/TableWizard.form
new file mode 100644
index 0000000..5af7395
--- /dev/null
+++ b/src/wizard/TableWizard.form
@@ -0,0 +1,467 @@
+
+
+
diff --git a/src/wizard/TableWizard.java b/src/wizard/TableWizard.java
new file mode 100644
index 0000000..46b20a2
--- /dev/null
+++ b/src/wizard/TableWizard.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package wizard;
+
+import static wizard.WizardTools.*;
+import static pooledit.Definitions.*;
+import treemodel.XMLTreeNode;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JFrame;
+import javax.swing.SpinnerNumberModel;
+import org.w3c.dom.Element;
+
+/**
+ *
+ * @author jkalmari
+ */
+public class TableWizard extends javax.swing.JPanel {
+
+ private TableGenerator tableGenerator;
+
+ private DefaultComboBoxModel fontModel;
+ private DefaultComboBoxModel lineModel;
+ private DefaultComboBoxModel fillModel1;
+ private DefaultComboBoxModel fillModel2;
+
+ /** Creates new form TableWizard */
+ public TableWizard() {
+ //initComponents();
+ }
+
+ public void setTableGenerator(TableGenerator tableGenerator) {
+ this.tableGenerator = tableGenerator;
+ Element root = tableGenerator.getRoot().getModel().getDocument().getDocumentElement();
+ fontModel = new DefaultComboBoxModel(WizardTools.findElements(root, FONTATTRIBUTES));
+ lineModel = new DefaultComboBoxModel(WizardTools.findElements(root, LINEATTRIBUTES));
+ fillModel1 = new DefaultComboBoxModel(WizardTools.findElementsWithEmpty(root, FILLATTRIBUTES));
+ fillModel2 = new DefaultComboBoxModel(WizardTools.findElementsWithEmpty(root, FILLATTRIBUTES));
+ initComponents();
+ }
+
+ /** This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+ jLabel2 = new javax.swing.JLabel();
+ jHeadingRowCheckBox = new javax.swing.JCheckBox();
+ jHeightSpinner = new javax.swing.JSpinner();
+ jHeadingColCheckBox = new javax.swing.JCheckBox();
+ jLabel4 = new javax.swing.JLabel();
+ jWidthSpinner = new javax.swing.JSpinner();
+ jSeparator1 = new javax.swing.JSeparator();
+ jLabel5 = new javax.swing.JLabel();
+ jLabel6 = new javax.swing.JLabel();
+ jCellWidthSpinner = new javax.swing.JSpinner();
+ jCellHeightSpinner = new javax.swing.JSpinner();
+ jButton1 = new javax.swing.JButton();
+ jButton3 = new javax.swing.JButton();
+ jLabel7 = new javax.swing.JLabel();
+ jLabel8 = new javax.swing.JLabel();
+ jCellsHorizontalSpinner = new javax.swing.JSpinner();
+ jCellsVerticalSpinner = new javax.swing.JSpinner();
+ jSeparator2 = new javax.swing.JSeparator();
+ jLabel9 = new javax.swing.JLabel();
+ jLabel10 = new javax.swing.JLabel();
+ jFontAttributeComboBox = new javax.swing.JComboBox();
+ jLineAttributeComboBox = new javax.swing.JComboBox();
+ jLabel12 = new javax.swing.JLabel();
+ jLabel13 = new javax.swing.JLabel();
+ jHorizontalJustificationComboBox = new javax.swing.JComboBox();
+ jVerticalJustificationComboBox = new javax.swing.JComboBox();
+ jLabel11 = new javax.swing.JLabel();
+ jLabel14 = new javax.swing.JLabel();
+ jFillAttribute1ComboBox = new javax.swing.JComboBox();
+ jFillAttribute2ComboBox = new javax.swing.JComboBox();
+ jLabel15 = new javax.swing.JLabel();
+ jOffsetSpinner = new javax.swing.JSpinner();
+ jLabel16 = new javax.swing.JLabel();
+ jScaleTextField = new javax.swing.JTextField();
+ jLabel17 = new javax.swing.JLabel();
+ jNroDecimalsSpinner = new javax.swing.JSpinner();
+
+ jLabel2.setText("Heading Row Height");
+
+ jHeadingRowCheckBox.setSelected(true);
+ jHeadingRowCheckBox.setText("Heading Row");
+ jHeadingRowCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jHeadingRowCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jHeightSpinner.setValue(12);
+ jHeightSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jHeightSpinnerStateChanged(evt);
+ }
+ });
+
+ jHeadingColCheckBox.setSelected(true);
+ jHeadingColCheckBox.setText("Heading Column");
+ jHeadingColCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jHeadingColCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jLabel4.setText("Heading Column Width");
+
+ jWidthSpinner.setValue(32);
+ jWidthSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jWidthSpinnerStateChanged(evt);
+ }
+ });
+
+ jLabel5.setText("Column Width");
+
+ jLabel6.setText("Row Height");
+
+ jCellWidthSpinner.setValue(10);
+ jCellWidthSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jCellWidthSpinnerStateChanged(evt);
+ }
+ });
+
+ jCellHeightSpinner.setValue(10);
+ jCellHeightSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jCellHeightSpinnerStateChanged(evt);
+ }
+ });
+
+ jButton1.setText("Ok");
+ jButton1.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButton1ActionPerformed(evt);
+ }
+ });
+
+ jButton3.setText("Refresh");
+ jButton3.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButton3ActionPerformed(evt);
+ }
+ });
+
+ jLabel7.setText("Columns");
+
+ jLabel8.setText("Rows");
+
+ jCellsHorizontalSpinner.setValue(5);
+ jCellsHorizontalSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jCellsHorizontalSpinnerStateChanged(evt);
+ }
+ });
+
+ jCellsVerticalSpinner.setValue(5);
+ jCellsVerticalSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jCellsVerticalSpinnerStateChanged(evt);
+ }
+ });
+
+ jLabel9.setText("Font attribute");
+
+ jLabel10.setText("Line attribute");
+
+ jFontAttributeComboBox.setModel(fontModel );
+
+ jLineAttributeComboBox.setModel(lineModel);
+
+ jLabel12.setText("Horizontal justifiation");
+
+ jLabel13.setText("Vertical justification");
+
+ jHorizontalJustificationComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Left", "Middle", "Right" }));
+ jHorizontalJustificationComboBox.setSelectedItem("Middle");
+
+ jVerticalJustificationComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Top", "Middle", "Bottom" }));
+ jVerticalJustificationComboBox.setSelectedItem("Middle");
+
+ jLabel11.setText("Fill attribute (cells)");
+
+ jLabel14.setText("Fill attribute (headings)");
+
+ jFillAttribute1ComboBox.setModel(fillModel1);
+
+ jFillAttribute2ComboBox.setModel(fillModel2);
+
+ jLabel15.setText("Scale");
+
+ jOffsetSpinner.setValue(0);
+ jOffsetSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jOffsetSpinnerStateChanged(evt);
+ }
+ });
+
+ jLabel16.setText("Offset");
+
+ jScaleTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
+ jScaleTextField.setText("1.0");
+ jScaleTextField.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ jScaleTextFieldFocusLost(evt);
+ }
+ });
+
+ jLabel17.setText("Number of Decimals");
+
+ jNroDecimalsSpinner.setValue(0);
+ jNroDecimalsSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jNroDecimalsSpinnerStateChanged(evt);
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(28, 28, 28)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel17)
+ .addComponent(jLabel15))
+ .addGap(18, 18, 18)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jScaleTextField)
+ .addComponent(jNroDecimalsSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 52, Short.MAX_VALUE))
+ .addGap(26, 26, 26)
+ .addComponent(jLabel16)
+ .addGap(23, 23, 23)
+ .addComponent(jOffsetSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(layout.createSequentialGroup()
+ .addGap(28, 28, 28)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel9)
+ .addComponent(jLabel12)
+ .addComponent(jLabel10)
+ .addComponent(jLabel13)
+ .addComponent(jLabel11)
+ .addComponent(jLabel14))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jVerticalJustificationComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jFillAttribute2ComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jFillAttribute1ComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jFontAttributeComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, 145, Short.MAX_VALUE)
+ .addComponent(jLineAttributeComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jHorizontalJustificationComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jButton1)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jButton3))))
+ .addGroup(layout.createSequentialGroup()
+ .addGap(28, 28, 28)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel2)
+ .addComponent(jLabel4)
+ .addComponent(jLabel5)
+ .addComponent(jLabel6))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(jCellHeightSpinner)
+ .addComponent(jHeightSpinner)
+ .addComponent(jWidthSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 50, Short.MAX_VALUE)
+ .addComponent(jCellWidthSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 51, Short.MAX_VALUE))
+ .addGap(27, 27, 27)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jHeadingRowCheckBox)
+ .addComponent(jHeadingColCheckBox)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel8)
+ .addComponent(jLabel7))
+ .addGap(14, 14, 14)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jCellsVerticalSpinner)
+ .addComponent(jCellsHorizontalSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 54, Short.MAX_VALUE)))))
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 313, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jSeparator2, javax.swing.GroupLayout.DEFAULT_SIZE, 316, Short.MAX_VALUE)))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(15, 15, 15)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel4)
+ .addComponent(jWidthSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jHeadingRowCheckBox))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel2)
+ .addComponent(jHeightSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jHeadingColCheckBox))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jCellWidthSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel8)
+ .addComponent(jCellsVerticalSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel5))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jCellHeightSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel7)
+ .addComponent(jCellsHorizontalSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel6))
+ .addGap(14, 14, 14)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jOffsetSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel16))
+ .addGroup(layout.createSequentialGroup()
+ .addGap(4, 4, 4)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jLabel15, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jScaleTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel17)
+ .addComponent(jNroDecimalsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
+ .addGap(15, 15, 15)
+ .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 12, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jFontAttributeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel9))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jFillAttribute1ComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel11))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel14)
+ .addComponent(jFillAttribute2ComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLineAttributeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel10))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel12)
+ .addComponent(jHorizontalJustificationComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jVerticalJustificationComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel13))
+ .addGap(14, 14, 14)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jButton1)
+ .addComponent(jButton3))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ }// //GEN-END:initComponents
+
+ private void jNroDecimalsSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jNroDecimalsSpinnerStateChanged
+ // TODO add your handling code here:
+ }//GEN-LAST:event_jNroDecimalsSpinnerStateChanged
+
+ private void jScaleTextFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jScaleTextFieldFocusLost
+ String text = jScaleTextField.getText();
+ double value = 1.0;
+ try {
+ value = Double.valueOf(text);
+ } catch (NumberFormatException ex) {
+ value = 1.0f;
+ }
+ jScaleTextField.setText(Double.toString(value));
+ }//GEN-LAST:event_jScaleTextFieldFocusLost
+
+ private void jOffsetSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jOffsetSpinnerStateChanged
+ // TODO add your handling code here:
+ }//GEN-LAST:event_jOffsetSpinnerStateChanged
+
+ private void jCellsVerticalSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jCellsVerticalSpinnerStateChanged
+ // TODO add your handling code here:
+ }//GEN-LAST:event_jCellsVerticalSpinnerStateChanged
+
+ private void jCellsHorizontalSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jCellsHorizontalSpinnerStateChanged
+ // TODO add your handling code here:
+ }//GEN-LAST:event_jCellsHorizontalSpinnerStateChanged
+
+ private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
+ // Refresh
+ if (tableGenerator != null) {
+ tableGenerator.update();
+ }
+ }//GEN-LAST:event_jButton3ActionPerformed
+
+ private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
+ // OK
+ if (tableGenerator != null) {
+ tableGenerator.update();
+ }
+ ((JFrame) this.getParent().getParent().getParent().getParent()).setVisible(false); //FIXME is this best way to close the window?
+ }//GEN-LAST:event_jButton1ActionPerformed
+
+ private void jCellHeightSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jCellHeightSpinnerStateChanged
+ checkSpinnerLimits(jCellHeightSpinner, 0, 65535);
+ }//GEN-LAST:event_jCellHeightSpinnerStateChanged
+
+ private void jCellWidthSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jCellWidthSpinnerStateChanged
+ checkSpinnerLimits(jCellWidthSpinner, 0, 65535);
+ }//GEN-LAST:event_jCellWidthSpinnerStateChanged
+
+ private void jWidthSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jWidthSpinnerStateChanged
+ checkSpinnerLimits(jWidthSpinner, 0, 65535);
+ }//GEN-LAST:event_jWidthSpinnerStateChanged
+
+ private void jHeightSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jHeightSpinnerStateChanged
+ checkSpinnerLimits(jHeightSpinner, 0, 65535);
+ }//GEN-LAST:event_jHeightSpinnerStateChanged
+
+ public boolean hasHeadingRow(){
+ return jHeadingRowCheckBox.getModel().isSelected();
+ }
+ public boolean hasHeadingColumn(){
+ return jHeadingColCheckBox.getModel().isSelected();
+ }
+ public int getHeadingColumnWidth() {
+ return (Integer) ((SpinnerNumberModel) jWidthSpinner.getModel()).getValue();
+ }
+ public int getHeadingRowHeight() {
+ return (Integer) ((SpinnerNumberModel) jHeightSpinner.getModel()).getValue();
+ }
+ public int getCellWidth() {
+ return (Integer) ((SpinnerNumberModel) jCellWidthSpinner.getModel()).getValue();
+ }
+ public int getCellHeight() {
+ return (Integer) ((SpinnerNumberModel) jCellHeightSpinner.getModel()).getValue();
+ }
+ public int getCellsHorizontal() {
+ return (Integer) ((SpinnerNumberModel) jCellsHorizontalSpinner.getModel()).getValue();
+ }
+ public int getCellsVertical() {
+ return (Integer) ((SpinnerNumberModel) jCellsVerticalSpinner.getModel()).getValue();
+ }
+ public String getFontAttribute(){
+ return (String) jFontAttributeComboBox.getModel().getSelectedItem();
+ }
+ public String getLineAttribute(){
+ return (String) jLineAttributeComboBox.getModel().getSelectedItem();
+ }
+ public String getFillAttributeCell(){
+ return (String) jFillAttribute1ComboBox.getModel().getSelectedItem();
+ }
+ public String getFillAttributeHeading(){
+ return (String) jFillAttribute2ComboBox.getModel().getSelectedItem();
+ }
+ public String getHorizontalJustification(){
+ return (String) jHorizontalJustificationComboBox.getModel().getSelectedItem();
+ }
+ public String getVerticalJustification(){
+ return (String) jVerticalJustificationComboBox.getModel().getSelectedItem();
+ }
+ public int getOffset(){
+ return (Integer) ((SpinnerNumberModel) jOffsetSpinner.getModel()).getValue();
+ }
+ public Double getScale() {
+ return Double.valueOf(jScaleTextField.getText());
+ }
+ public int getNumberOfDecimals() {
+ return (Integer) ((SpinnerNumberModel) jNroDecimalsSpinner.getModel()).getValue();
+ }
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton jButton1;
+ private javax.swing.JButton jButton3;
+ private javax.swing.JSpinner jCellHeightSpinner;
+ private javax.swing.JSpinner jCellWidthSpinner;
+ private javax.swing.JSpinner jCellsHorizontalSpinner;
+ private javax.swing.JSpinner jCellsVerticalSpinner;
+ private javax.swing.JComboBox jFillAttribute1ComboBox;
+ private javax.swing.JComboBox jFillAttribute2ComboBox;
+ private javax.swing.JComboBox jFontAttributeComboBox;
+ private javax.swing.JCheckBox jHeadingColCheckBox;
+ private javax.swing.JCheckBox jHeadingRowCheckBox;
+ private javax.swing.JSpinner jHeightSpinner;
+ private javax.swing.JComboBox jHorizontalJustificationComboBox;
+ private javax.swing.JLabel jLabel10;
+ private javax.swing.JLabel jLabel11;
+ private javax.swing.JLabel jLabel12;
+ private javax.swing.JLabel jLabel13;
+ private javax.swing.JLabel jLabel14;
+ private javax.swing.JLabel jLabel15;
+ private javax.swing.JLabel jLabel16;
+ private javax.swing.JLabel jLabel17;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel4;
+ private javax.swing.JLabel jLabel5;
+ private javax.swing.JLabel jLabel6;
+ private javax.swing.JLabel jLabel7;
+ private javax.swing.JLabel jLabel8;
+ private javax.swing.JLabel jLabel9;
+ private javax.swing.JComboBox jLineAttributeComboBox;
+ private javax.swing.JSpinner jNroDecimalsSpinner;
+ private javax.swing.JSpinner jOffsetSpinner;
+ private javax.swing.JTextField jScaleTextField;
+ private javax.swing.JSeparator jSeparator1;
+ private javax.swing.JSeparator jSeparator2;
+ private javax.swing.JComboBox jVerticalJustificationComboBox;
+ private javax.swing.JSpinner jWidthSpinner;
+ // End of variables declaration//GEN-END:variables
+
+}
diff --git a/src/wizard/TrendGenerator.java b/src/wizard/TrendGenerator.java
new file mode 100644
index 0000000..8ee1d38
--- /dev/null
+++ b/src/wizard/TrendGenerator.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package wizard;
+
+import static wizard.WizardTools.*;
+import static pooledit.Definitions.*;
+import font.BitmapFont;
+import java.awt.Dimension;
+import org.w3c.dom.Element;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class TrendGenerator {
+
+ private TrendWizard wiz;
+ private XMLTreeNode root;
+ private String name;
+
+ /** Creates a new instance of TrendGenerator */
+ public TrendGenerator(TrendWizard wiz, XMLTreeNode root, String name) {
+ this.wiz = wiz;
+ this.root = root;
+ this.name = name;
+ wiz.setTrendGenerator(this);
+ }
+
+ public XMLTreeNode getRoot() {
+ return root;
+ }
+
+ /**
+ * This method is called when the table should be updated
+ */
+ public void update() {
+ int width = wiz.getContainerWidth();
+ int height = wiz.getContainerHeight();
+ Element container = createContainer(root.actual(), name, width, height);
+
+ if (root.isType(OBJECTS)) {
+ setAttributeIfMissing(container, POS_X, "0");
+ setAttributeIfMissing(container, POS_Y, "0");
+ }
+
+ if (wiz.isHorizontal()) {
+ updateHorizontal(container, width, height);
+ }
+ else {
+ updateVertical(container, width, height);
+ }
+ }
+
+ private void updateVertical(Element container, int width, int height) {
+ boolean growPos = !wiz.isGrowsLeftDown();
+ int nroNumbers = wiz.getNumbers();
+ int max = wiz.getMaxValue();
+ int min = wiz.getMinValue();
+ String maxValue = Integer.toString(max);
+ String minValue = Integer.toString(min);
+
+ String fontName = wiz.getFontAttribute();
+ Element font = root.getModel().getElementByName(fontName);
+ Dimension fontdim = BitmapFont.nameToDimension(font.getAttribute(FONT_SIZE));
+ int xPad = fontdim.width / 2 + 1;
+ int yPad = fontdim.height / 2 + 1;
+ int nroWidth = 0;
+ int nroHeight = 0;
+ if (wiz.isDrawNumbers()) {
+ nroWidth = fontdim.width * maxValue.length();
+ }
+ if (wiz.isDrawTitle()) {
+ nroHeight = fontdim.height;
+ }
+
+ int nroBars = wiz.getNroBars();
+ int barWidth = (width - nroWidth - 2 * xPad) / nroBars;
+ int barHeight = height - nroHeight - 2 * yPad;
+
+ Element bg = createRectangle(container, "background", width, height,
+ wiz.getLineAttribute1(), wiz.getFillAttribute1(), false);
+ setIncludeAttributes(bg, 0, 0);
+
+ if (wiz.isDrawNumbers()) {
+ if (nroNumbers == 1) {
+ int nro = (min + max) / 2;
+ int posY = barHeight / 2;
+ Element label = createString(container, "label0", nroWidth,
+ fontdim.height, Integer.toString(nro), fontName);
+ setIncludeAttributes(label, 0, posY);
+ removeExtraElements(container, "label", 1);
+ }
+ else if (nroNumbers > 1) {
+ int nroInc = (max - min) / (nroNumbers - 1);
+ int posYInc = barHeight / (nroNumbers - 1);
+ for (int i = 0, nro = min, posY = 1; i < nroNumbers;
+ i++, nro += nroInc, posY += posYInc) {
+ Element label = createString(container, "label" + i, nroWidth,
+ fontdim.height, Integer.toString(growPos ? max - nro : nro), fontName);
+ setIncludeAttributes(label, 2, posY);
+ }
+ removeExtraElements(container, "label", nroNumbers);
+ }
+ else {
+ removeExtraElements(container, "label", 0);
+ }
+ }
+ else {
+ removeExtraElements(container, "label", 0);
+ }
+
+ if (wiz.isDrawTitle()) {
+ String title = wiz.getTitle();
+ int titleWidth = fontdim.width * title.length();
+ Element tle = createString(container, "title0", titleWidth,
+ fontdim.height, title, fontName);
+ setIncludeAttributes(tle, (width - titleWidth) / 2, height - nroHeight - 2);
+ }
+ else {
+ removeExtraElements(container, "title", 0);
+ }
+
+ Element panel = createRectangle(container, "panel", nroBars * barWidth + 2, barHeight + 2,
+ wiz.getLineAttribute2(), wiz.getFillAttribute2(), false);
+ setIncludeAttributes(panel, nroWidth + xPad - 1, yPad - 1);
+
+ String barColor = wiz.getBarColor();
+ int nroTicks = wiz.getNroTicks();
+ boolean drawTicks = wiz.isDrawTicks();
+ boolean drawBorder = wiz.isDrawBorder();
+ int valueInc = (max - min) / (nroBars - 1);
+ for (int i = 0, posX = nroWidth + xPad, value = min; i < nroBars; i++, posX += barWidth, value += valueInc) {
+
+ Element bar = createBarGraph(container, "bar" + i, barColor, barWidth, barHeight,
+ max, min, value, nroTicks, drawTicks, growPos, drawBorder);
+ setIncludeAttributes(bar, posX, yPad);
+ }
+ removeExtraElements(container, "bar", nroBars);
+ }
+
+
+ private void updateHorizontal(Element container, int width, int height) {
+
+ }
+
+ private Element createBarGraph(Element father, String name, String color,
+ int width, int height, int maxValue, int minValue, int value,
+ int nroTicks, boolean drawTicks, boolean growPos, boolean drawBorder) {
+
+ Element bar = createElement(LINEARBARGRAPH, name, father, false);
+ setAttribute(bar, COLOUR, color);
+ setAttribute(bar, WIDTH, width);
+ setAttribute(bar, HEIGHT, height);
+ setAttribute(bar, MAX_VALUE, maxValue);
+ setAttribute(bar, MIN_VALUE, minValue);
+ setAttribute(bar, VALUE, value);
+ setAttribute(bar, NUMBER_OF_TICKS, nroTicks);
+ setOptions(bar, new String[] {"border", "ticks", "growpositive"},
+ new boolean[] {drawBorder, drawTicks, growPos});
+
+ return bar;
+ }
+
+ private static void setOptions(Element elem, String[] names, boolean[] values) {
+ String oldOpt = elem.getAttribute(OPTIONS);
+ String newOpt = oldOpt;
+
+ for (int i = 0; i < names.length; i++) {
+ // setting attribute
+ if (values[i]) {
+ if (!newOpt.contains(names[i])) {
+ newOpt = newOpt.concat("+" + names[i]);
+ }
+ }
+ // resetting attribute
+ else {
+ newOpt = newOpt.replace(names[i], "");
+ }
+ }
+ // trim attribute
+ newOpt.replace("++", "+");
+ if (newOpt.startsWith("+")) {
+ newOpt = newOpt.substring(1);
+ }
+ if (newOpt.endsWith("+")) {
+ newOpt = newOpt.substring(0, newOpt.length() - 1);
+ }
+ if (!newOpt.equals(oldOpt)) {
+ elem.setAttribute(OPTIONS, newOpt);
+ }
+ }
+}
diff --git a/src/wizard/TrendWizard.form b/src/wizard/TrendWizard.form
new file mode 100644
index 0000000..f2ca7f8
--- /dev/null
+++ b/src/wizard/TrendWizard.form
@@ -0,0 +1,497 @@
+
+
+
diff --git a/src/wizard/TrendWizard.java b/src/wizard/TrendWizard.java
new file mode 100644
index 0000000..cdd9473
--- /dev/null
+++ b/src/wizard/TrendWizard.java
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package wizard;
+
+import static wizard.WizardTools.*;
+import static pooledit.Definitions.*;
+import attributetable.AttributeTable;
+import color.ColorPalette;
+import javax.swing.JFrame;
+import javax.swing.ListCellRenderer;
+import javax.swing.DefaultComboBoxModel;
+import org.w3c.dom.Element;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class TrendWizard extends javax.swing.JPanel {
+
+ private TrendGenerator trendGenerator;
+
+ private final String[] colors = ColorPalette.getAllColorNames();
+ private DefaultComboBoxModel colorModel = new DefaultComboBoxModel(colors);
+ private ListCellRenderer colorRenderer = AttributeTable.createColorListRenderer();
+ private DefaultComboBoxModel fillModel1;
+ private DefaultComboBoxModel lineModel1;
+ private DefaultComboBoxModel fillModel2;
+ private DefaultComboBoxModel lineModel2;
+
+ private DefaultComboBoxModel fontModel;
+
+
+ /** Creates new form BeanForm */
+ public TrendWizard() {
+ // initComponents();
+ }
+
+ public void setTrendGenerator(TrendGenerator trendGenerator) {
+ this.trendGenerator = trendGenerator;
+ Element root = trendGenerator.getRoot().getModel().getDocument().getDocumentElement();
+ fillModel1 = new DefaultComboBoxModel(WizardTools.findElementsWithEmpty(root, FILLATTRIBUTES));
+ lineModel1 = new DefaultComboBoxModel(WizardTools.findElements(root, LINEATTRIBUTES));
+ fillModel2 = new DefaultComboBoxModel(WizardTools.findElementsWithEmpty(root, FILLATTRIBUTES));
+ lineModel2 = new DefaultComboBoxModel(WizardTools.findElements(root, LINEATTRIBUTES));
+
+ fontModel = new DefaultComboBoxModel(WizardTools.findElements(root, FONTATTRIBUTES));
+ initComponents();
+ }
+
+ /** This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+ jLabel1 = new javax.swing.JLabel();
+ jLabel2 = new javax.swing.JLabel();
+ jWidthSpinner = new javax.swing.JSpinner();
+ jHeightSpinner = new javax.swing.JSpinner();
+ jLabel3 = new javax.swing.JLabel();
+ jLabel4 = new javax.swing.JLabel();
+ jMinValueSpinner = new javax.swing.JSpinner();
+ jMaxValueSpinner = new javax.swing.JSpinner();
+ jLabel5 = new javax.swing.JLabel();
+ jNroTicksSpinner = new javax.swing.JSpinner();
+ jDrawTicksCB = new javax.swing.JCheckBox();
+ jHorizontalCB = new javax.swing.JCheckBox();
+ jGrowsLeftDownCB = new javax.swing.JCheckBox();
+ jSeparator1 = new javax.swing.JSeparator();
+ jLabel6 = new javax.swing.JLabel();
+ jLabel7 = new javax.swing.JLabel();
+ jBarColorCB = new javax.swing.JComboBox();
+ jSeparator2 = new javax.swing.JSeparator();
+ jLabel8 = new javax.swing.JLabel();
+ jFillAttribute2CB = new javax.swing.JComboBox();
+ jNroBarsSpinner = new javax.swing.JSpinner();
+ jLabel9 = new javax.swing.JLabel();
+ jLineAttribute2CB = new javax.swing.JComboBox();
+ jLabel10 = new javax.swing.JLabel();
+ jFillAttribute1CB = new javax.swing.JComboBox();
+ jLabel11 = new javax.swing.JLabel();
+ jLineAttribute1CB = new javax.swing.JComboBox();
+ jLabel12 = new javax.swing.JLabel();
+ jTitleTF = new javax.swing.JTextField();
+ jLabel13 = new javax.swing.JLabel();
+ jFontAttribCB = new javax.swing.JComboBox();
+ jShowTitleCB = new javax.swing.JCheckBox();
+ jLabel14 = new javax.swing.JLabel();
+ jNumbersSpinner = new javax.swing.JSpinner();
+ jDrawNumbersCB = new javax.swing.JCheckBox();
+ jRefreshButton = new javax.swing.JButton();
+ jOKButton = new javax.swing.JButton();
+ jDrawBorderCB = new javax.swing.JCheckBox();
+
+ jLabel1.setText("Width");
+
+ jLabel2.setText("Height");
+
+ jWidthSpinner.setValue(100);
+ jWidthSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jWidthSpinnerStateChanged(evt);
+ }
+ });
+
+ jHeightSpinner.setValue(100);
+ jHeightSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jHeightSpinnerStateChanged(evt);
+ }
+ });
+
+ jLabel3.setText("Min Value");
+
+ jLabel4.setText("Max Value");
+
+ jMinValueSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jMinValueSpinnerStateChanged(evt);
+ }
+ });
+
+ jMaxValueSpinner.setValue(100);
+ jMaxValueSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jMaxValueSpinnerStateChanged(evt);
+ }
+ });
+
+ jLabel5.setText("Nro Ticks");
+
+ jNroTicksSpinner.setValue(11);
+ jNroTicksSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jNroTicksSpinnerStateChanged(evt);
+ }
+ });
+
+ jDrawTicksCB.setSelected(true);
+ jDrawTicksCB.setText("Draw Ticks");
+ jDrawTicksCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jDrawTicksCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jHorizontalCB.setText("Horizontal");
+ jHorizontalCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jHorizontalCB.setEnabled(false);
+ jHorizontalCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jGrowsLeftDownCB.setText("Grows Left / Down");
+ jGrowsLeftDownCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jGrowsLeftDownCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jLabel6.setText("Nro Bars");
+
+ jLabel7.setText("Bar Color");
+
+ jBarColorCB.setModel(colorModel);
+ jBarColorCB.setRenderer(colorRenderer);
+
+ jLabel8.setText("Fill Attribute");
+
+ jFillAttribute2CB.setModel(fillModel2);
+
+ jNroBarsSpinner.setValue(6);
+ jNroBarsSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jNroBarsSpinnerStateChanged(evt);
+ }
+ });
+
+ jLabel9.setText("Line Attribute");
+
+ jLineAttribute2CB.setModel(lineModel2);
+
+ jLabel10.setText("Fill Attribute");
+
+ jFillAttribute1CB.setModel(fillModel1);
+
+ jLabel11.setText("Line Attribute");
+
+ jLineAttribute1CB.setModel(lineModel1);
+
+ jLabel12.setText("Title");
+
+ jTitleTF.setText("trend");
+
+ jLabel13.setText("Font Attribute");
+
+ jFontAttribCB.setModel(fontModel);
+
+ jShowTitleCB.setSelected(true);
+ jShowTitleCB.setText("Show Title");
+ jShowTitleCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jShowTitleCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jLabel14.setText("Numbers");
+
+ jNumbersSpinner.setValue(6);
+ jNumbersSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ jNumbersSpinnerStateChanged(evt);
+ }
+ });
+
+ jDrawNumbersCB.setSelected(true);
+ jDrawNumbersCB.setText("Draw Numbers");
+ jDrawNumbersCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jDrawNumbersCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ jRefreshButton.setText("Refresh");
+ jRefreshButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jRefreshButtonActionPerformed(evt);
+ }
+ });
+
+ jOKButton.setText("OK");
+ jOKButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jOKButtonActionPerformed(evt);
+ }
+ });
+
+ jDrawBorderCB.setText("Draw Border");
+ jDrawBorderCB.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ jDrawBorderCB.setMargin(new java.awt.Insets(0, 0, 0, 0));
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel1)
+ .addComponent(jLabel2))
+ .addGap(26, 26, 26)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(jHeightSpinner)
+ .addComponent(jWidthSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 48, Short.MAX_VALUE))
+ .addGap(16, 16, 16)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jGrowsLeftDownCB)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel11)
+ .addComponent(jLabel10))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jFillAttribute1CB, javax.swing.GroupLayout.Alignment.TRAILING, 0, 99, Short.MAX_VALUE)
+ .addComponent(jLineAttribute1CB, 0, 99, Short.MAX_VALUE)))))
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel6))
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel3))
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel5))
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel4))
+ .addComponent(jLabel14))
+ .addGap(8, 8, 8)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jNumbersSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 50, Short.MAX_VALUE)
+ .addComponent(jNroTicksSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 50, Short.MAX_VALUE)
+ .addComponent(jMaxValueSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 50, Short.MAX_VALUE)
+ .addComponent(jMinValueSpinner, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 50, Short.MAX_VALUE)
+ .addComponent(jNroBarsSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 50, Short.MAX_VALUE))
+ .addGap(14, 14, 14)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jDrawNumbersCB)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jDrawTicksCB)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 22, Short.MAX_VALUE)
+ .addComponent(jDrawBorderCB))
+ .addComponent(jLabel7)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel8)
+ .addComponent(jLabel9))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jBarColorCB, javax.swing.GroupLayout.Alignment.TRAILING, 0, 99, Short.MAX_VALUE)
+ .addComponent(jFillAttribute2CB, javax.swing.GroupLayout.Alignment.TRAILING, 0, 99, Short.MAX_VALUE)
+ .addComponent(jLineAttribute2CB, 0, 99, Short.MAX_VALUE)))))
+ .addComponent(jHorizontalCB)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jShowTitleCB)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel12)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jTitleTF, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(16, 16, 16)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel13)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jFontAttribCB, 0, 96, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addComponent(jOKButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jRefreshButton)))))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED))
+ .addComponent(jSeparator2, javax.swing.GroupLayout.DEFAULT_SIZE, 289, Short.MAX_VALUE)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 289, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(jWidthSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jFillAttribute1CB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel10))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel2)
+ .addComponent(jHeightSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel11)
+ .addComponent(jLineAttribute1CB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jHorizontalCB)
+ .addComponent(jGrowsLeftDownCB))
+ .addGap(15, 15, 15)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel6)
+ .addComponent(jNroBarsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel7)
+ .addComponent(jBarColorCB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel3)
+ .addComponent(jMinValueSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel8)
+ .addComponent(jFillAttribute2CB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel4)
+ .addComponent(jMaxValueSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel9)
+ .addComponent(jLineAttribute2CB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jNroTicksSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jDrawTicksCB)
+ .addComponent(jDrawBorderCB))
+ .addComponent(jLabel5))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel14)
+ .addComponent(jNumbersSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jDrawNumbersCB))
+ .addGap(14, 14, 14)
+ .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 11, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel12)
+ .addComponent(jTitleTF, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel13)
+ .addComponent(jFontAttribCB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jShowTitleCB)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 7, Short.MAX_VALUE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jOKButton)
+ .addComponent(jRefreshButton))
+ .addContainerGap())
+ );
+ }// //GEN-END:initComponents
+
+ private void jHeightSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jHeightSpinnerStateChanged
+ checkSpinnerLimits(jHeightSpinner, 0, 65535);
+ }//GEN-LAST:event_jHeightSpinnerStateChanged
+
+ private void jWidthSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jWidthSpinnerStateChanged
+ checkSpinnerLimits(jWidthSpinner, 0, 65535);
+ }//GEN-LAST:event_jWidthSpinnerStateChanged
+
+ private void jNumbersSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jNumbersSpinnerStateChanged
+ checkSpinnerLimits(jNumbersSpinner, 0, 99); // 10 might be too little
+ }//GEN-LAST:event_jNumbersSpinnerStateChanged
+
+ private void jNroTicksSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jNroTicksSpinnerStateChanged
+ checkSpinnerLimits(jNroTicksSpinner, 0, 99);
+ }//GEN-LAST:event_jNroTicksSpinnerStateChanged
+
+ private void jMaxValueSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jMaxValueSpinnerStateChanged
+ checkSpinnerLimits(jMaxValueSpinner, 0, 65535);
+ }//GEN-LAST:event_jMaxValueSpinnerStateChanged
+
+ private void jMinValueSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jMinValueSpinnerStateChanged
+ checkSpinnerLimits(jMinValueSpinner, 0, 65535);
+ }//GEN-LAST:event_jMinValueSpinnerStateChanged
+
+ private void jNroBarsSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jNroBarsSpinnerStateChanged
+ checkSpinnerLimits(jNroBarsSpinner, 0, 99);
+ }//GEN-LAST:event_jNroBarsSpinnerStateChanged
+
+ private void jRefreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jRefreshButtonActionPerformed
+ if (trendGenerator != null) {
+ trendGenerator.update();
+ }
+ }//GEN-LAST:event_jRefreshButtonActionPerformed
+
+ private void jOKButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jOKButtonActionPerformed
+ if (trendGenerator != null) {
+ trendGenerator.update();
+ }
+ ((JFrame) this.getParent().getParent().getParent().getParent()).setVisible(false);
+ }//GEN-LAST:event_jOKButtonActionPerformed
+
+ public String getBarColor() {
+ return (String) jBarColorCB.getSelectedItem();
+ }
+ public boolean isDrawBorder() {
+ return jDrawBorderCB.isSelected();
+ }
+ public boolean isDrawNumbers() {
+ return jDrawNumbersCB.isSelected();
+ }
+ public boolean isDrawTicks() {
+ return jDrawTicksCB.isSelected();
+ }
+ public String getFillAttribute1() {
+ return (String) jFillAttribute1CB.getSelectedItem();
+ }
+ public String getFillAttribute2() {
+ return (String) jFillAttribute2CB.getSelectedItem();
+ }
+ public String getFontAttribute() {
+ return (String) jFontAttribCB.getSelectedItem();
+ }
+ public boolean isGrowsLeftDown() {
+ return jGrowsLeftDownCB.isSelected();
+ }
+ public int getContainerHeight() {
+ return (Integer) jHeightSpinner.getValue();
+ }
+ public boolean isHorizontal() {
+ return jHorizontalCB.isSelected();
+ }
+ public String getLineAttribute1() {
+ return (String) jLineAttribute1CB.getSelectedItem();
+ }
+ public String getLineAttribute2() {
+ return (String) jLineAttribute2CB.getSelectedItem();
+ }
+ public int getMaxValue() {
+ return (Integer) jMaxValueSpinner.getValue();
+ }
+ public int getMinValue() {
+ return (Integer) jMinValueSpinner.getValue();
+ }
+ public int getNroBars() {
+ return (Integer) jNroBarsSpinner.getValue();
+ }
+ public int getNroTicks() {
+ return (Integer) jNroTicksSpinner.getValue();
+ }
+ public int getNumbers() {
+ return (Integer) jNumbersSpinner.getValue();
+ }
+ public boolean isDrawTitle() {
+ return jShowTitleCB.isSelected();
+ }
+ public String getTitle() {
+ return jTitleTF.getText();
+ }
+ public int getContainerWidth() {
+ return (Integer) jWidthSpinner.getValue();
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JComboBox jBarColorCB;
+ private javax.swing.JCheckBox jDrawBorderCB;
+ private javax.swing.JCheckBox jDrawNumbersCB;
+ private javax.swing.JCheckBox jDrawTicksCB;
+ private javax.swing.JComboBox jFillAttribute1CB;
+ private javax.swing.JComboBox jFillAttribute2CB;
+ private javax.swing.JComboBox jFontAttribCB;
+ private javax.swing.JCheckBox jGrowsLeftDownCB;
+ private javax.swing.JSpinner jHeightSpinner;
+ private javax.swing.JCheckBox jHorizontalCB;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel10;
+ private javax.swing.JLabel jLabel11;
+ private javax.swing.JLabel jLabel12;
+ private javax.swing.JLabel jLabel13;
+ private javax.swing.JLabel jLabel14;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel3;
+ private javax.swing.JLabel jLabel4;
+ private javax.swing.JLabel jLabel5;
+ private javax.swing.JLabel jLabel6;
+ private javax.swing.JLabel jLabel7;
+ private javax.swing.JLabel jLabel8;
+ private javax.swing.JLabel jLabel9;
+ private javax.swing.JComboBox jLineAttribute1CB;
+ private javax.swing.JComboBox jLineAttribute2CB;
+ private javax.swing.JSpinner jMaxValueSpinner;
+ private javax.swing.JSpinner jMinValueSpinner;
+ private javax.swing.JSpinner jNroBarsSpinner;
+ private javax.swing.JSpinner jNroTicksSpinner;
+ private javax.swing.JSpinner jNumbersSpinner;
+ private javax.swing.JButton jOKButton;
+ private javax.swing.JButton jRefreshButton;
+ private javax.swing.JSeparator jSeparator1;
+ private javax.swing.JSeparator jSeparator2;
+ private javax.swing.JCheckBox jShowTitleCB;
+ private javax.swing.JTextField jTitleTF;
+ private javax.swing.JSpinner jWidthSpinner;
+ // End of variables declaration//GEN-END:variables
+
+}
diff --git a/src/wizard/WizardTools.java b/src/wizard/WizardTools.java
new file mode 100644
index 0000000..ccccb56
--- /dev/null
+++ b/src/wizard/WizardTools.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2007 Automation technology laboratory,
+ * Helsinki University of Technology
+ *
+ * Visit automation.tkk.fi for information about the automation
+ * technology laboratory.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+package wizard;
+
+import static pooledit.Definitions.*;
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerModel;
+import multidom.SingleDOM;
+import org.w3c.dom.Node;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import pooledit.Tools;
+import treemodel.XMLTreeNode;
+
+/**
+ *
+ * @author mohman
+ */
+public class WizardTools {
+
+ /**
+ * Creates a new instance of WizardTools
+ */
+ public WizardTools() {
+ }
+
+ public static void checkSpinnerLimits(JSpinner spinner, int min, int max) {
+ SpinnerModel model = spinner.getModel();
+ Integer value = (Integer) model.getValue();
+ if (value < min) {
+ model.setValue(min);
+ }
+ if (value > max) {
+ model.setValue(max);
+ }
+ }
+
+ public static Element createContainer(Element father, String name, int width, int height) {
+ Element container = createElement(CONTAINER, name, father, false);
+ setAttribute(container, WIDTH, width);
+ setAttribute(container, HEIGHT, height);
+ return container;
+ }
+
+ public static Element createString(Element father, String name, int width, int height,
+ String value, String fontAttribute) {
+
+ Element str = createElement(OUTPUTSTRING, name, father, false);
+ setAttribute(str, WIDTH, width);
+ setAttribute(str, HEIGHT, height);
+ setAttribute(str, VALUE, value);
+ setAttribute(str, HORIZONTAL_JUSTIFICATION, "middle");
+ setAttribute(str, OPTIONS, "transparent");
+ setAttribute(str, BACKGROUND_COLOUR, "white");
+
+ createIncludeRoleElement(fontAttribute, FONT_ATTRIBUTES, str);
+ return str;
+ }
+
+ public static Element createString(Element father, String name, int width, int height,
+ String value, String fontAttribute, String justification) {
+
+ Element str = createElement(OUTPUTSTRING, name, father, false);
+ setAttribute(str, WIDTH, width);
+ setAttribute(str, HEIGHT, height);
+ setAttributeIfEmpty(str, VALUE, value);
+ setAttribute(str, HORIZONTAL_JUSTIFICATION, justification);
+ setAttribute(str, OPTIONS, "transparent");
+ setAttribute(str, BACKGROUND_COLOUR, "white");
+
+ createIncludeRoleElement(fontAttribute, FONT_ATTRIBUTES, str);
+ return str;
+ }
+
+ public static Element createRectangle(Element father, String name, int width, int height,
+ String lineAttribute, String fillAttribute, boolean first) {
+
+ Element rectangle = createElement(RECTANGLE, name, father, first);
+ setAttribute(rectangle, WIDTH, width);
+ setAttribute(rectangle, HEIGHT, height);
+ createIncludeRoleElement(lineAttribute, LINE_ATTRIBUTES, rectangle);
+ createIncludeRoleElement(fillAttribute, FILL_ATTRIBUTES, rectangle);
+ return rectangle;
+ }
+
+ /**
+ * Creates a new element to document and adds it as a child
+ * if name is null a new name is created
+ * if father is null, element will be added to root
+ * if first is true the element is created as first child
+ */
+ public static Element createElement(String type, String name, Element father, boolean first) {
+
+ // iterate ovet father's direct children to see if it already has
+ // the specified child
+ NodeList children = father.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ if (elem.getNodeName() == type &&
+ elem.getAttribute(NAME).equals(name)) {
+ return elem;
+ }
+ }
+ }
+
+ // if new element has to be created, we do it this way to the default
+ // attributes "free" from the schema
+ Document tmp = Tools.parseDocument("<" + type + "/>",
+ SingleDOM.SCHEMA);
+ Element elem = (Element) father.getOwnerDocument().adoptNode(tmp.getDocumentElement()).getFirstChild();
+ elem.setAttribute(NAME, name);
+ if (first) {
+ father.insertBefore(elem, father.getFirstChild());
+ }
+ else {
+ father.appendChild(elem);
+ }
+ return elem;
+ }
+
+ /**
+ * Creates a new include object child element that points to an object
+ * called "name". Removes any other child elements that have the same role.
+ * If name is null, no new element is created and the method returns null.
+ */
+ public static Element createIncludeRoleElement(String name, String role, Element father) {
+ Element rv = null;
+ List worklist = new ArrayList();
+ NodeList children = father.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ if (elem.getAttribute(ROLE).equals(role)) {
+ if (elem.getAttribute(NAME).equals(name)) {
+ rv = elem;
+ }
+ else {
+ worklist.add(elem);
+ }
+ }
+ }
+ }
+ for (Element elem : worklist) {
+ father.removeChild(elem);
+ }
+
+ if (rv == null && name != null) {
+ // if new element has to be created, we do it this way to the default
+ // attributes "free" from the schema
+ Document tmp = Tools.parseDocument("",
+ SingleDOM.SCHEMA);
+ rv = (Element) father.getOwnerDocument().adoptNode(tmp.getDocumentElement()).getFirstChild();
+ rv.setAttribute(NAME, name);
+ rv.setAttribute(ROLE, role);
+ father.appendChild(rv);
+ }
+ return rv;
+ }
+
+ public static void removeExtraElements(Element father, String name, int start) {
+ NodeList children = father.getChildNodes();
+ for (int i = children.getLength() - 1; i >= 0; i--) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ String fullname = elem.getAttribute(NAME);
+ if (fullname.startsWith(name)) {
+ try {
+ String end = fullname.substring(name.length());
+ int nro = Integer.parseInt(end);
+ if (nro >= start) {
+ father.removeChild(elem);
+ }
+ }
+ catch (NumberFormatException e) {}
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the element's attribute only if it has been changed.
+ */
+ public static void setAttribute(Element elem, String name, Object value) {
+ String val = value == null ? null : value.toString();
+ if (!elem.getAttribute(name).equals(val)) {
+ elem.setAttribute(name, val);
+ }
+ }
+
+ /**
+ * Sets the element's attribute only if it does not exist.
+ */
+ public static void setAttributeIfMissing(Element elem, String name, Object value) {
+ if (!elem.hasAttribute(name)) {
+ setAttribute(elem, name, value);
+ }
+ }
+
+ public static void setAttributeIfEmpty(Element elem, String name, Object value) {
+ if (elem.getAttribute(name).isEmpty()) {
+ setAttribute(elem, name, value);
+ }
+ }
+
+ public static void setIncludeAttributes(Element element, int posX, int posY){
+ setAttribute(element, POS_X, Integer.toString(posX));
+ setAttribute(element, POS_Y, Integer.toString(posY));
+ }
+
+ /**
+ * Finds all elements of the given type *at the root level* and returns
+ * their names.
+ */
+ public static List findElementNames(Element root, String tagname) {
+ List names = new ArrayList();
+ NodeList children = root.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node node = children.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element elem = (Element) node;
+ if (elem.getNodeName().equals(tagname)) {
+ names.add(elem.getAttribute(NAME));
+ }
+ }
+ }
+ return names;
+ }
+
+ public static Object[] findElements(Element root, String tagname) {
+ List names = findElementNames(root, tagname);
+ return names.toArray();
+ }
+
+ /**
+ * The same as findElements but includes a null element at the beginning
+ * of the array.
+ */
+ public static Object[] findElementsWithEmpty(Element root, String tagname) {
+ List names = findElementNames(root, tagname);
+ names.add(0, null);
+ return names.toArray();
+ }
+}
diff --git a/template.xml b/template.xml
new file mode 100644
index 0000000..96d38cf
--- /dev/null
+++ b/template.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test.xml b/test.xml
new file mode 100644
index 0000000..9cfa164
--- /dev/null
+++ b/test.xml
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+