[svn-upgrade] Integrating new upstream version, libmicroba-java (0.4.4.3)
gregor herrmann
15 years ago
0 | Manifest-Version: 1.0 | |
1 | Ant-Version: Apache Ant 1.7.0 | |
2 | Created-By: 10.0-b22 (Sun Microsystems Inc.) | |
3 |
0 | <project name="microba" default="mix_release"> | |
1 | ||
2 | ||
3 | <property name="version" value="0.4.4.3" /> | |
4 | <property name="src-pack" value="microba-${version}-sources.jar" /> | |
5 | <property name="bin-pack" value="microba-${version}.jar" /> | |
6 | <property name="doc-pack" value="microba-${version}-javadoc.jar" /> | |
7 | <property name="all-pack" value="microba-${version}-full.zip" /> | |
8 | <property name="maven-pack" value="microba-${version}-maven.jar" /> | |
9 | ||
10 | <path id="compile-classpath"> | |
11 | <pathelement location="lib-compiletime/jgraph.jar" /> | |
12 | </path> | |
13 | ||
14 | <target name="src_release"> | |
15 | <mkdir dir="redist" /> | |
16 | ||
17 | <jar destfile="redist\${src-pack}"> | |
18 | <fileset dir="." includes="src/**/*.*" /> | |
19 | <fileset dir="." includes="lib-compiletime/**/*.txt" /> | |
20 | <fileset dir="." includes="license.txt" /> | |
21 | <fileset dir="." includes="readme.txt" /> | |
22 | <fileset dir="." includes="change.log.txt" /> | |
23 | <fileset dir="." includes="build.xml" /> | |
24 | </jar> | |
25 | </target> | |
26 | ||
27 | <target name="bin_release"> | |
28 | <condition property="javamail.complete"> | |
29 | <and> | |
30 | <available classname="javax.activation.DataHandler" /> | |
31 | <available classname="javax.mail.Transport" /> | |
32 | </and> | |
33 | </condition> | |
34 | ||
35 | ||
36 | <mkdir dir="compile" /> | |
37 | <mkdir dir="redist" /> | |
38 | ||
39 | <echo>IMPORTANT: Assuming libraries in lib-compiletime directory. </echo> | |
40 | <javac srcdir="src" debug="true" destdir="compile" target="1.4" source="1.4" classpathref="compile-classpath"> | |
41 | <exclude name="**/demo/**/*.*" /> | |
42 | </javac> | |
43 | ||
44 | <jar destfile="redist\${bin-pack}"> | |
45 | <fileset dir="compile" includes="**/*.*" /> | |
46 | <fileset dir="src/main/java"> | |
47 | <include name="**/*.png" /> | |
48 | <include name="**/*.properties" /> | |
49 | </fileset> | |
50 | <fileset dir="." includes="license.txt" /> | |
51 | <manifest> | |
52 | <section name="common"> | |
53 | <attribute name="Specification-Title" value="Microba" /> | |
54 | <attribute name="Specification-Version" value="${version}" /> | |
55 | <attribute name="Specification-Vendor" value="Michael Baranov" /> | |
56 | <attribute name="Implementation-Title" value="Microba" /> | |
57 | <attribute name="Implementation-Version" value="${version}" /> | |
58 | <attribute name="Implementation-Vendor" value="Michael Baranov" /> | |
59 | <attribute name="Author" value="Michael Baranov" /> | |
60 | </section> | |
61 | <section name="com/michaelbaranov/microba/calendar/CalendarPane.class"> | |
62 | <attribute name="Java-Bean" value="True" /> | |
63 | </section> | |
64 | <section name="com/michaelbaranov/microba/calendar/DatePicker.class"> | |
65 | <attribute name="Java-Bean" value="True" /> | |
66 | </section> | |
67 | <section name="com/michaelbaranov/microba/gradient/GradientBar.class"> | |
68 | <attribute name="Java-Bean" value="True" /> | |
69 | </section> | |
70 | <section name="com/michaelbaranov/microba/gradienteditor/GradientEditor.class"> | |
71 | <attribute name="Java-Bean" value="True" /> | |
72 | </section> | |
73 | <section name="com/michaelbaranov/microba/marker/MarkerBar.class"> | |
74 | <attribute name="Java-Bean" value="True" /> | |
75 | </section> | |
76 | <section name="com/michaelbaranov/microba/jgrpah/birdview/Birdview.class"> | |
77 | <attribute name="Java-Bean" value="True" /> | |
78 | </section> | |
79 | ||
80 | </manifest> | |
81 | </jar> | |
82 | ||
83 | <delete dir="compile" /> | |
84 | </target> | |
85 | ||
86 | <target name="doc_release"> | |
87 | <mkdir dir="javadoc" /> | |
88 | <mkdir dir="redist" /> | |
89 | ||
90 | <javadoc access="public" author="yes" destdir="javadoc"> | |
91 | <fileset dir="src"> | |
92 | <include name="**/*.java" /> | |
93 | <exclude name="**/demo/**/*.java" /> | |
94 | </fileset> | |
95 | </javadoc> | |
96 | ||
97 | <jar destfile="redist\${doc-pack}"> | |
98 | <fileset dir="javadoc" includes="**/*.*" /> | |
99 | </jar> | |
100 | ||
101 | <delete dir="javadoc" /> | |
102 | </target> | |
103 | ||
104 | <target name="mix_release"> | |
105 | <antcall target="bin_release" /> | |
106 | <antcall target="src_release" /> | |
107 | <antcall target="doc_release" /> | |
108 | ||
109 | <zip destfile="redist\${all-pack}"> | |
110 | <fileset dir="redist"> | |
111 | <include name="${bin-pack}" /> | |
112 | <include name="${doc-pack}" /> | |
113 | <include name="${src-pack}" /> | |
114 | </fileset> | |
115 | </zip> | |
116 | ||
117 | <jar destfile="redist\${maven-pack}"> | |
118 | <fileset dir="redist"> | |
119 | <include name="${bin-pack}" /> | |
120 | <include name="${doc-pack}" /> | |
121 | <include name="${src-pack}" /> | |
122 | </fileset> | |
123 | <fileset dir="."> | |
124 | <include name="pom.xml"/> | |
125 | </fileset> | |
126 | </jar> | |
127 | </target> | |
128 | ||
129 | ||
130 | ||
131 | </project>⏎ |
0 | v.0.4.4.3 | |
1 | * Added: CalendarPane.observeMonth(int, int); | |
2 | * Added: Slovak and Czech translation (thanks to Lubomir Grajciar) | |
3 | * Added: Hebrew translation (thanks to Moshe Elisha) | |
4 | * Added: Turkish translation (thanks to Emre Yazici) | |
5 | * Added: Hungarian translation (thanks to Zoltan Kuroli) | |
6 | ||
7 | v.0.4.4.2 | |
8 | * changed misspelled package name 'jgrpah' to 'jgraph' | |
9 | * fixed build script to correctly package resources (thanks to Velimir Gasparovic) | |
10 | ||
11 | v.0.4.4.1 | |
12 | * Change: changed project directory structure according to Maven guidelines. | |
13 | * Fixed: DatePicker popup did not close when clicked on already selecteed date. | |
14 | * CalendarPane.fireCommitEvent(boolean) made public | |
15 | * CalendarPane.fireActionEvent() made public | |
16 | ||
17 | v.0.4.4 | |
18 | * Added: DatePicker.dropdownFocusable property | |
19 | * Added: MicrobaComponent.colorOverrideMap property | |
20 | * Added: Microba.setColorOverrideMap method | |
21 | These two improvements allow to override colors on per-instance and per-L&F basis. | |
22 | Currenty CalendarPane and DatePicker support some color overriding. | |
23 | * Fixed: CalendarPane ignores enabled property | |
24 | ||
25 | v.0.4.3.4 (21 nov 2006) | |
26 | * Fixed: Microba works with NetBeans Matisse GUI editor. | |
27 | Thanks to Witold Szczerba for testing the fix. | |
28 | * Fixed: manifest now specifies javabeans | |
29 | * Refactored: common superclass extracted: MicrobaComponent | |
30 | ||
31 | v.0.4.3.3 | |
32 | * Fixed: DatePicker ignores locale passed in constructor | |
33 | ||
34 | v.0.4.3.2 | |
35 | * Fixed: DatePicker ignores showWeekNumber | |
36 | * Fixed: DatePicker may return wrong date via getDate() in some cases | |
37 | * Fixed: Portuguese translation corrected by Roger Araújo | |
38 | ||
39 | v.0.4.3.1 | |
40 | * Fixed: DatePicker displays no tooltip string | |
41 | * Fixed: DatePicker goes to infinite loop if keepTime is true, and date format has no time fields. | |
42 | Thanks to Wolfgang Fietzke for his excellent bug report! | |
43 | ||
44 | v0.4.3 | |
45 | * Added: Birdview component for JGraph library | |
46 | * Added: Swedish translation for DatePicker | |
47 | * Added: CalendarPane.stripDate ( and DatePicker.stripDate ) property (optional time stripping) | |
48 | * Added: DatePicker.keepTime property (improved time field handling) | |
49 | * Added: DatePicker.pickerStyle property (controls visual style of the picker itself) | |
50 | * Added: DatePicker.showButtonOnly() shortcut method | |
51 | * Added: static CalendarPane.stripDate(...) utility method | |
52 | * Fixed: DatePicker.setDateFormat(...) never fired a PropertyChangeEvent | |
53 | * Changed: picker-16.png ico for DatePicker changed | |
54 | ||
55 | v.0.4.2 (31 jul 2006) | |
56 | * Refactored: dateStyle property of DatePicker was dropped in favour of dateFormat property. However, | |
57 | old constructors preserved. | |
58 | * Fixed: OutOfMemory exception bug fixed. The bug prevented components to be garbage collected because | |
59 | references were held by Timer tasks. Regression: CalendarPane will update 'today' date only | |
60 | on creation. | |
61 | ||
62 | v.0.4.1 (18 jun 2006) | |
63 | * Added: ability to show number of weeks for DatePicker and CalendarPane. | |
64 | * Fixed: Applet refresh problem: see readme.txt, see Microba.init() | |
65 | * Fixed: Polish translation (thanks to Gregory Kaczmarczyk) | |
66 | ||
67 | v.0.4 (29 mar 2006) | |
68 | * Refactored: com.michaelbaranov.microba.common package introduced | |
69 | * Refactored: class HollidayPolicy renamed to HolidayPolicy | |
70 | * Refactored: properties named 'hollidayPolicy' renamed to 'holidayPolicy' in all classes | |
71 | * Refactored: renamed recources in com.michaelbaranov.microba.calendar.resource: | |
72 | 16.png -> picker-16.png | |
73 | l.png -> back-16.png | |
74 | ll.png -> back-fast-16.png | |
75 | r.png -> forward-16.png | |
76 | rr.png -> forward-fast-16.png | |
77 | * Added: MarkerBar, GradientBar, GradientEditor controls | |
78 | * Added: multiple translations for CalendarPane UI | |
79 | * Added: enabled/disabled state for DatePicker and CalendarPane | |
80 | * Fixed: DatePickerCellEditor not respected focus lost behavior of the DatePicker. | |
81 | Now it is possible to commit cell edit when cell edit stops (e.g. tab out). | |
82 | ||
83 | ||
84 | ||
85 | v.0.3 (15 dec 2005) | |
86 | * First public release | |
87 | ||
88 | ⏎ |
0 | Copyright (c) 2005-2006, Michael Baranov | |
1 | All rights reserved. | |
2 | ||
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: | |
4 | ||
5 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. | |
6 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. | |
7 | * Neither the name of the MICROBA, MICHAELBARANOV.COM, MICHAEL BARANOV nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. | |
8 | ||
9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.⏎ |
0 | TIPS & TRICKS: | |
1 | 1. In Applet#init(), call Microba.init() to handle browser refresh button correctly. | |
2 | ||
3 | CONTRIBUTORS: | |
4 | (random order) | |
5 | * Alessandro Falappa: Italian translation | |
6 | * Frido van Orden, Henk van Voorthuijsen: Dutch translation | |
7 | * Gustavo Santucho: Spanish translation | |
8 | * Gregory Kaczmarczyk: Polish translation | |
9 | * Philipp Meier: German translation | |
10 | * Claus Nielsen: Danish translation | |
11 | * Felix Bordea: Romanian translation | |
12 | * David Ekholm: Swedish translation | |
13 | ||
14 | TRANSLATION: | |
15 | For the list of supported language translations, look into | |
16 | com\michaelbaranov\microba\calendar\DefaultCalendarResources.properties | |
17 | ||
18 | Help translating needed! Please contribute! | |
19 | Send the translations to: michael[.]baranov[@]gmail[.]com | |
20 | ||
21 | KNOWN PROBLEMS: | |
22 | ||
23 | 1. DatePicker: while editing the field, enter arbitrary string after the last formatted character, | |
24 | position cursor anywhere past the last formatted character, press up or down key -> exception is | |
25 | raised: | |
26 | ||
27 | java.lang.IllegalArgumentException: Invalid index | |
28 | ... | |
29 | ||
30 | Reason: a bug in Sun's javax.swing.text.InternationalFormatter. | |
31 | ||
32 | 2. DatePicker: set the style to STYLE_MODERN, open dropdown, click month combo box -> dropdown | |
33 | is hidden OR can not close dropdown anymore OR exception (depends on JRE version). | |
34 | ||
35 | Reason: a bug in Sun's javax.swing.JPopupMenu, when using "popup in popup". | |
36 | ||
37 | 3. DatePicker: if a heavy-weight popup is used to display dropdown (for ex. it goes outside a JFrame), | |
38 | dropdown does not receive keyboard focus anymore. | |
39 | ||
40 | Reason: a bug in Sun's focus handling routines.⏎ |
0 | import java.awt.FlowLayout; | |
1 | import java.awt.event.ActionEvent; | |
2 | import java.awt.event.ActionListener; | |
3 | import java.beans.PropertyVetoException; | |
4 | ||
5 | import javax.swing.JButton; | |
6 | import javax.swing.JFrame; | |
7 | ||
8 | import com.michaelbaranov.microba.calendar.DatePicker; | |
9 | ||
10 | public class Snippet { | |
11 | ||
12 | public static void main(String[] args) { | |
13 | JFrame frame = new JFrame(); | |
14 | frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); | |
15 | frame.setLayout(new FlowLayout()); | |
16 | ||
17 | final DatePicker picker = new DatePicker(); | |
18 | picker.addActionListener(new ActionListener() { | |
19 | public void actionPerformed(ActionEvent e) { | |
20 | System.out.println(picker.getDate()); | |
21 | } | |
22 | }); | |
23 | ||
24 | JButton btn = new JButton("NULL"); | |
25 | btn.addActionListener(new ActionListener() { | |
26 | public void actionPerformed(ActionEvent e) { | |
27 | try { | |
28 | picker.setDate(null); | |
29 | } catch (PropertyVetoException e1) { | |
30 | System.out.println("Could not set null date."); | |
31 | e1.printStackTrace(); | |
32 | } | |
33 | picker.setEnabled(false); | |
34 | } | |
35 | }); | |
36 | frame.add(picker); | |
37 | frame.add(btn); | |
38 | frame.setSize(640, 480); | |
39 | frame.setVisible(true); | |
40 | } | |
41 | } |
0 | package com.michaelbaranov.microba; | |
1 | ||
2 | import java.applet.Applet; | |
3 | import java.awt.Color; | |
4 | import java.beans.PropertyChangeEvent; | |
5 | import java.beans.PropertyChangeListener; | |
6 | import java.util.HashMap; | |
7 | import java.util.Map; | |
8 | ||
9 | import javax.swing.LookAndFeel; | |
10 | import javax.swing.UIManager; | |
11 | ||
12 | import com.michaelbaranov.microba.common.MicrobaComponent; | |
13 | ||
14 | /** | |
15 | * This class is used to initialize Microba library. | |
16 | * | |
17 | * @author Michael Baranov | |
18 | * | |
19 | */ | |
20 | public class Microba { | |
21 | ||
22 | private static UIChangeListener changeListener = new UIChangeListener(); | |
23 | ||
24 | /** | |
25 | * Initializes the library: installs L&F properties, sets up a L&F change | |
26 | * listener. | |
27 | * <p> | |
28 | * No need to call this method explicitly for desktop applications. You | |
29 | * should only call it in {@link Applet#init()}. This will handle browser | |
30 | * refresh button correctly. | |
31 | * | |
32 | */ | |
33 | public static synchronized void init() { | |
34 | setLookAndFeelProperties(UIManager.getLookAndFeel()); | |
35 | ||
36 | UIManager.removePropertyChangeListener(changeListener); | |
37 | UIManager.addPropertyChangeListener(changeListener); | |
38 | } | |
39 | ||
40 | private static synchronized void setLookAndFeelProperties( | |
41 | LookAndFeel lookAndFeel) { | |
42 | if (lookAndFeel == null) | |
43 | return; | |
44 | ||
45 | String packagePrefix = "com.michaelbaranov.microba."; | |
46 | ||
47 | // all L&F | |
48 | UIManager.put("microba.CalendarPaneUI", packagePrefix | |
49 | + "calendar.ui.basic.BasicCalendarPaneUI"); | |
50 | UIManager.put("microba.DatePickerUI", packagePrefix | |
51 | + "calendar.ui.basic.BasicDatePickerUI"); | |
52 | UIManager.put("microba.GradientUI", packagePrefix | |
53 | + "gradient.ui.basic.BasicGradientUI"); | |
54 | UIManager.put("microba.GradientEditorUI", packagePrefix | |
55 | + "gradienteditor.ui.basic.BasicGradientEditorUI"); | |
56 | UIManager.put("microba.MarkerBarUI", packagePrefix | |
57 | + "marker.ui.basic.BasicMarkerBarUI"); | |
58 | ||
59 | // particular L&F | |
60 | if (lookAndFeel.getID().equals("Windows")) { | |
61 | UIManager.put("microba.MarkerBarUI", packagePrefix | |
62 | + "marker.ui.windows.WindowsMarkerBarUI"); | |
63 | } else if (lookAndFeel.getID().equals("Metal")) { | |
64 | UIManager.put("microba.MarkerBarUI", packagePrefix | |
65 | + "marker.ui.metal.MetalMarkerBarUI"); | |
66 | } else if (lookAndFeel.getID().equals("Motif")) { | |
67 | UIManager.put("microba.MarkerBarUI", packagePrefix | |
68 | + "marker.ui.motif.MotifMarkerBarUI"); | |
69 | } | |
70 | ||
71 | } | |
72 | ||
73 | private static final class UIChangeListener implements | |
74 | PropertyChangeListener { | |
75 | public void propertyChange(PropertyChangeEvent event) { | |
76 | if ("lookAndFeel".equals(event.getPropertyName())) { | |
77 | setLookAndFeelProperties((LookAndFeel) event.getNewValue()); | |
78 | } | |
79 | } | |
80 | } | |
81 | ||
82 | private static Map lookAndFeelToOverride = new HashMap(); | |
83 | ||
84 | /** | |
85 | * Sets per-Lokk&Feel map of color overrides. | |
86 | * | |
87 | * | |
88 | * @param lookAndFeel | |
89 | * look&feel ID | |
90 | * @param overrides | |
91 | * keys in the map are {@link String} constants, valuse are of | |
92 | * type {@link Color} or of type {@link String} (in this case, | |
93 | * {@link Color} values are obtained via | |
94 | * {@link UIManager#getColor(Object)}). May be <code>null</code>. | |
95 | */ | |
96 | public static void setColorOverrideMap(String lookAndFeel, Map overrides) { | |
97 | lookAndFeelToOverride.put(lookAndFeel, overrides); | |
98 | // TODO: refresh ui delegates | |
99 | } | |
100 | ||
101 | /** | |
102 | * Returns overriden color for given component in current Look&Feel. The | |
103 | * algorithms is: | |
104 | * <ul> | |
105 | * <li>If the component overrides the constant (per-instance override), | |
106 | * then it is returned. | |
107 | * <li>If the library overrides the constant (per-Look&Feel override), then | |
108 | * it is returned. | |
109 | * <li>Else <code>null</code> is returned. | |
110 | * </ul> | |
111 | * This method is actually intended to be used by UI delegates of the | |
112 | * library. | |
113 | * | |
114 | * @param colorConstant | |
115 | * color constant | |
116 | * @param component | |
117 | * component of the library | |
118 | * @return overriden color or <code>null</code> if not overriden | |
119 | */ | |
120 | public static synchronized Color getOverridenColor(String colorConstant, | |
121 | MicrobaComponent component) { | |
122 | ||
123 | Map componentOverrideMap = component.getColorOverrideMap(); | |
124 | if (componentOverrideMap != null) { | |
125 | if (componentOverrideMap.containsKey(colorConstant)) { | |
126 | Object val = componentOverrideMap.get(colorConstant); | |
127 | if (val instanceof Color) | |
128 | return (Color) val; | |
129 | else | |
130 | return UIManager.getColor(val); | |
131 | } | |
132 | } | |
133 | ||
134 | String currentLookAndFeel = UIManager.getLookAndFeel().getID(); | |
135 | Map overrides = (Map) lookAndFeelToOverride.get(currentLookAndFeel); | |
136 | if (overrides != null) { | |
137 | if (overrides.containsKey(colorConstant)) { | |
138 | Object val = overrides.get(colorConstant); | |
139 | if (val instanceof Color) | |
140 | return (Color) val; | |
141 | else | |
142 | return UIManager.getColor(val); | |
143 | ||
144 | } | |
145 | } | |
146 | ||
147 | return null; | |
148 | } | |
149 | ||
150 | /** | |
151 | * Returns overriden color for given component in current Look&Feel or a | |
152 | * default value. The algorithms is: | |
153 | * <ul> | |
154 | * <li>If the component overrides the constant (per-instance override), | |
155 | * then it is returned. | |
156 | * <li>If the library overrides the constant (per-Look&Feel override), then | |
157 | * it is returned. | |
158 | * <li>Else defaultColor is returned. | |
159 | * </ul> | |
160 | * This method is actually intended to be used by UI delegates of the | |
161 | * library. | |
162 | * | |
163 | * @param colorConstant | |
164 | * color constant | |
165 | * @param component | |
166 | * component of the library | |
167 | * @param defaultColor | |
168 | * @return overriden color or defaultColor if not overriden | |
169 | */ | |
170 | public static synchronized Color getOverridenColor(String colorConstant, | |
171 | MicrobaComponent component, Color defaultColor) { | |
172 | Color overriden = getOverridenColor(colorConstant, component); | |
173 | if (overriden != null) | |
174 | return overriden; | |
175 | else | |
176 | return defaultColor; | |
177 | } | |
178 | ||
179 | } |
0 | package com.michaelbaranov.microba.calendar; | |
1 | ||
2 | public interface CalendarColors { | |
3 | ||
4 | // *** Header | |
5 | ||
6 | public static final String COLOR_CALENDAR_HEADER_FOREGROUND_ENABLED = "calendar.header.foreground.enabled"; | |
7 | ||
8 | public static final String COLOR_CALENDAR_HEADER_FOREGROUND_DISABLED = "calendar.header.foreground.disabled"; | |
9 | ||
10 | public static final String COLOR_CALENDAR_HEADER_BACKGROUND_ENABLED = "calendar.header.background.enabled"; | |
11 | ||
12 | public static final String COLOR_CALENDAR_HEADER_BACKGROUND_DISABLED = "calendar.header.background.disabled"; | |
13 | ||
14 | public static final String COLOR_CALENDAR_HEADER_FOREGROUND_WEEKEND_ENABLED = "calendar.header.foreground.weekend.enabled"; | |
15 | ||
16 | public static final String COLOR_CALENDAR_HEADER_FOREGROUND_WEEKEND_DISABLED = "calendar.header.foreground.weekend.disabled"; | |
17 | ||
18 | // *** Dates grid | |
19 | public static final String COLOR_CALENDAR_GRID_FOCUS = "calendar.grid.focus"; | |
20 | ||
21 | public static final String COLOR_CALENDAR_GRID_RESTRICTED = "calendar.grid.banned"; | |
22 | ||
23 | // | |
24 | public static final String COLOR_CALENDAR_GRID_BACKGROUND_ENABLED = "calendar.grid.background.enabled"; | |
25 | ||
26 | public static final String COLOR_CALENDAR_GRID_BACKGROUND_DISABLED = "calendar.grid.background.disabled"; | |
27 | ||
28 | public static final String COLOR_CALENDAR_GRID_FOREGROUND_ENABLED = "calendar.grid.foreground.enabled"; | |
29 | ||
30 | public static final String COLOR_CALENDAR_GRID_FOREGROUND_DISABLED = "calendar.grid.foreground.disabled"; | |
31 | ||
32 | // | |
33 | public static final String COLOR_CALENDAR_GRID_SELECTION_BACKGROUND_ENABLED = "calendar.grid.selection.background.enabled"; | |
34 | ||
35 | public static final String COLOR_CALENDAR_GRID_SELECTION_BACKGROUND_DISABLED = "calendar.grid.selection.background.disabled"; | |
36 | ||
37 | public static final String COLOR_CALENDAR_GRID_WEEKEND_FOREGROUND_ENABLED = "calendar.grid.weekend.foreground.enabled"; | |
38 | ||
39 | public static final String COLOR_CALENDAR_GRID_WEEKEND_FOREGROUND_DISABLED = "calendar.grid.weekend.foreground.disabled"; | |
40 | ||
41 | public static final String COLOR_CALENDAR_GRID_HOLIDAY_FOREGROUND_ENABLED = "calendar.grid.holiday.foreground.enabled"; | |
42 | ||
43 | public static final String COLOR_CALENDAR_GRID_HOLIDAY_FOREGROUND_DISABLED = "calendar.grid.holiday.foreground.disabled"; | |
44 | ||
45 | } |
0 | package com.michaelbaranov.microba.calendar; | |
1 | ||
2 | import java.awt.event.ActionEvent; | |
3 | import java.awt.event.ActionListener; | |
4 | import java.beans.PropertyChangeEvent; | |
5 | import java.beans.PropertyChangeListener; | |
6 | import java.beans.PropertyVetoException; | |
7 | import java.util.Calendar; | |
8 | import java.util.Date; | |
9 | import java.util.Locale; | |
10 | import java.util.TimeZone; | |
11 | ||
12 | import javax.swing.JFormattedTextField; | |
13 | import javax.swing.event.EventListenerList; | |
14 | ||
15 | import com.michaelbaranov.microba.calendar.ui.CalendarPaneUI; | |
16 | import com.michaelbaranov.microba.common.CommitEvent; | |
17 | import com.michaelbaranov.microba.common.CommitListener; | |
18 | import com.michaelbaranov.microba.common.MicrobaComponent; | |
19 | ||
20 | /** | |
21 | * A concrete implementation of JComponent. Capable of displaying and selecting | |
22 | * dates, much like a real-world calendar. | |
23 | * | |
24 | * @author Michael Baranov | |
25 | */ | |
26 | public class CalendarPane extends MicrobaComponent implements CalendarColors { | |
27 | ||
28 | /** | |
29 | * The name of a "date" property. | |
30 | */ | |
31 | public static final String PROPERTY_NAME_DATE = "date"; | |
32 | ||
33 | /** | |
34 | * The name of a "locale" property. | |
35 | */ | |
36 | public static final String PROPERTY_NAME_LOCALE = "locale"; | |
37 | ||
38 | /** | |
39 | * The name of a "zone" property. | |
40 | */ | |
41 | public static final String PROPERTY_NAME_ZONE = "zone"; | |
42 | ||
43 | /** | |
44 | * The name of a "style" property. | |
45 | */ | |
46 | public static final String PROPERTY_NAME_STYLE = "style"; | |
47 | ||
48 | /** | |
49 | * The name of a "showTodayButton" property. | |
50 | */ | |
51 | public static final String PROPERTY_NAME_SHOW_TODAY_BTN = "showTodayButton"; | |
52 | ||
53 | /** | |
54 | * The name of a "showNoneButton" property. | |
55 | */ | |
56 | public static final String PROPERTY_NAME_SHOW_NONE_BTN = "showNoneButton"; | |
57 | ||
58 | /** | |
59 | * The name of a "focusLocatBehavior" property. | |
60 | */ | |
61 | public static final String PROPERTY_NAME_FOCUS_LOST_BEHAVIOR = "focusLostBehavior"; | |
62 | ||
63 | /** | |
64 | * The name of a "vetoPolicy" property. | |
65 | */ | |
66 | public static final String PROPERTY_NAME_VETO_POLICY = "vetoPlicy"; | |
67 | ||
68 | /** | |
69 | * The name of a "holidayPolicy" property. | |
70 | */ | |
71 | public static final String PROPERTY_NAME_HOLIDAY_POLICY = "holidayPolicy"; | |
72 | ||
73 | /** | |
74 | * The name of a "resources" property. | |
75 | */ | |
76 | public static final String PROPERTY_NAME_RESOURCES = "resources"; | |
77 | ||
78 | /** | |
79 | * The name of a "resources" property. | |
80 | */ | |
81 | public static final String PROPERTY_NAME_SHOW_NUMBER_WEEK = "showNumberOfWeek"; | |
82 | ||
83 | /** | |
84 | * The name of a "stripTime" property. | |
85 | */ | |
86 | public static final String PROPERTY_NAME_STRIP_TIME = "stripTime"; | |
87 | ||
88 | /** | |
89 | * A constant for the "style" property. | |
90 | */ | |
91 | public static final int STYLE_MODERN = 0x10; | |
92 | ||
93 | /** | |
94 | * A constant for the "style" property. | |
95 | */ | |
96 | public static final int STYLE_CLASSIC = 0x20; | |
97 | ||
98 | private static final String uiClassID = "microba.CalendarPaneUI"; | |
99 | ||
100 | private EventListenerList commitListenerList = new EventListenerList(); | |
101 | ||
102 | private EventListenerList actionListenerList = new EventListenerList(); | |
103 | ||
104 | private Date date; | |
105 | ||
106 | private TimeZone zone; | |
107 | ||
108 | private Locale locale; | |
109 | ||
110 | private VetoPolicy vetoPolicy; | |
111 | ||
112 | private HolidayPolicy holidayPolicy; | |
113 | ||
114 | private CalendarResources resources; | |
115 | ||
116 | private int style; | |
117 | ||
118 | private boolean showTodayButton; | |
119 | ||
120 | private boolean showNoneButton; | |
121 | ||
122 | private int focusLostBehavior; | |
123 | ||
124 | private boolean showNumberOfWeek; | |
125 | ||
126 | private boolean stripTime; | |
127 | ||
128 | public String getUIClassID() { | |
129 | return uiClassID; | |
130 | } | |
131 | ||
132 | /** | |
133 | * Constructor. | |
134 | */ | |
135 | public CalendarPane() { | |
136 | this(null, 0, Locale.getDefault(), TimeZone.getDefault()); | |
137 | } | |
138 | ||
139 | /** | |
140 | * Constructor. | |
141 | */ | |
142 | public CalendarPane(int style) { | |
143 | this(null, style, Locale.getDefault(), TimeZone.getDefault()); | |
144 | } | |
145 | ||
146 | /** | |
147 | * Constructor. | |
148 | */ | |
149 | public CalendarPane(Date initialDate) { | |
150 | this(initialDate, 0, Locale.getDefault(), TimeZone.getDefault()); | |
151 | } | |
152 | ||
153 | /** | |
154 | * Constructor. | |
155 | */ | |
156 | public CalendarPane(Date initialDate, int style) { | |
157 | this(initialDate, style, Locale.getDefault(), TimeZone.getDefault()); | |
158 | } | |
159 | ||
160 | /** | |
161 | * Constructor. | |
162 | */ | |
163 | public CalendarPane(Date initialDate, int style, Locale locale) { | |
164 | this(initialDate, style, locale, TimeZone.getDefault()); | |
165 | } | |
166 | ||
167 | /** | |
168 | * Constructor. | |
169 | */ | |
170 | public CalendarPane(Date initialDate, int style, Locale locale, | |
171 | TimeZone zone) { | |
172 | checkStyle(style); | |
173 | checkLocale(locale); | |
174 | checkTimeZone(zone); | |
175 | this.style = style; | |
176 | this.date = initialDate; | |
177 | this.locale = locale; | |
178 | this.zone = zone; | |
179 | this.focusLostBehavior = JFormattedTextField.COMMIT_OR_REVERT; | |
180 | this.showTodayButton = true; | |
181 | this.showNoneButton = true; | |
182 | this.vetoPolicy = null; | |
183 | this.resources = new DefaultCalendarResources(); | |
184 | this.stripTime = true; | |
185 | ||
186 | // forward date property change to action event | |
187 | addPropertyChangeListener(PROPERTY_NAME_DATE, | |
188 | new PropertyChangeListener() { | |
189 | public void propertyChange(PropertyChangeEvent evt) { | |
190 | fireActionEvent(); | |
191 | } | |
192 | }); | |
193 | ||
194 | updateUI(); | |
195 | } | |
196 | ||
197 | /** | |
198 | * Returns currently selected date in the control. | |
199 | * <p> | |
200 | * The returned date is guaranteed to pass the restriction check by the | |
201 | * current {@link VetoPolicy}. Based on the value of {@link #stripTime} | |
202 | * property, the returned date may be automatically stripped. | |
203 | * | |
204 | * @return currently selected date | |
205 | * @see #stripTime | |
206 | * @see #stripTime(Date, TimeZone, Locale) | |
207 | */ | |
208 | public Date getDate() { | |
209 | if (this.stripTime) | |
210 | return stripTime(date, getZone(), getLocale()); | |
211 | else | |
212 | return date; | |
213 | } | |
214 | ||
215 | /** | |
216 | * Sets currently selected date to the control. | |
217 | * <p> | |
218 | * The given date is checked against the current {@link VetoPolicy}. If the | |
219 | * check is passed, the date is transferred to the control and the control | |
220 | * is updated to display the date. | |
221 | * <p> | |
222 | * A {@link PropertyChangeEvent} may be fired, an {@link ActionEvent} may be | |
223 | * fired. | |
224 | * | |
225 | * @param date | |
226 | * the date to set | |
227 | * @throws PropertyVetoException | |
228 | * if the date is restricted by the current {@link VetoPolicy}. | |
229 | * @see #getVetoPolicy() | |
230 | * @see #setVetoPolicy(VetoPolicy) | |
231 | * @see #addActionListener(ActionListener) | |
232 | */ | |
233 | public void setDate(Date date) throws PropertyVetoException { | |
234 | if (!checkDate(date)) { | |
235 | PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent( | |
236 | this, PROPERTY_NAME_DATE, this.date, date); | |
237 | throw new PropertyVetoException( | |
238 | "Value vetoed by current vetoPolicy", propertyChangeEvent); | |
239 | } | |
240 | Date old = this.date; | |
241 | this.date = date; | |
242 | if (old != null || date != null) { | |
243 | firePropertyChange(PROPERTY_NAME_DATE, old, date); | |
244 | } | |
245 | } | |
246 | ||
247 | /** | |
248 | * Returns current locale. | |
249 | * | |
250 | * @return current locale | |
251 | */ | |
252 | public Locale getLocale() { | |
253 | return locale; | |
254 | } | |
255 | ||
256 | /** | |
257 | * Sets current locale. | |
258 | * <p> | |
259 | * The locale is used to construct internal {@link Calendar} instances and | |
260 | * affects visual representation of the control. | |
261 | * | |
262 | * @param locale | |
263 | * the locale to set | |
264 | */ | |
265 | public void setLocale(Locale locale) { | |
266 | Locale old = getLocale(); | |
267 | this.locale = locale; | |
268 | firePropertyChange(PROPERTY_NAME_LOCALE, old, getLocale()); | |
269 | } | |
270 | ||
271 | /** | |
272 | * Returns current time zone. | |
273 | * | |
274 | * @return current time zone | |
275 | */ | |
276 | public TimeZone getZone() { | |
277 | return zone; | |
278 | } | |
279 | ||
280 | /** | |
281 | * Sets current time zone. | |
282 | * <p> | |
283 | * The time zone is used to construct internal {@link Calendar} instances | |
284 | * and affects visual representation of the control. The dates returned by | |
285 | * {@link #getDate() } will have all time components set to zero considering | |
286 | * the current locale. | |
287 | * | |
288 | * @param zone | |
289 | * the time zone to set | |
290 | */ | |
291 | public void setZone(TimeZone zone) { | |
292 | TimeZone old = getZone(); | |
293 | this.zone = zone; | |
294 | firePropertyChange(PROPERTY_NAME_ZONE, old, getZone()); | |
295 | } | |
296 | ||
297 | /** | |
298 | * Returns current visual style of the control. | |
299 | * | |
300 | * @return current visual style constant. | |
301 | */ | |
302 | public int getStyle() { | |
303 | return style; | |
304 | } | |
305 | ||
306 | /** | |
307 | * Sets the current visual style of the control. | |
308 | * <p> | |
309 | * The control is then updated to reflect the new style. | |
310 | * | |
311 | * @param style | |
312 | * the style to set | |
313 | * @see #STYLE_CLASSIC | |
314 | * @see #STYLE_MODERN | |
315 | */ | |
316 | public void setStyle(int style) { | |
317 | style = checkStyle(style); | |
318 | int old = this.style; | |
319 | this.style = style; | |
320 | firePropertyChange(PROPERTY_NAME_STYLE, old, style); | |
321 | } | |
322 | ||
323 | /** | |
324 | * Is today button visible? | |
325 | * <p> | |
326 | * The today button allows the user to quickly select current date. | |
327 | * | |
328 | * @return <code>true</code> if the today button is visible, | |
329 | * <code>false</code> otherwise | |
330 | */ | |
331 | public boolean isShowTodayButton() { | |
332 | return showTodayButton; | |
333 | } | |
334 | ||
335 | /** | |
336 | * Shows or hides the today-button. | |
337 | * <p> | |
338 | * The today-button allows the user to quickly select current date. | |
339 | * | |
340 | * @param visible | |
341 | * <code>true</code> to show the today-button <code>false</code> | |
342 | * to hide | |
343 | */ | |
344 | public void setShowTodayButton(boolean visible) { | |
345 | Boolean old = new Boolean(this.showTodayButton); | |
346 | this.showTodayButton = visible; | |
347 | firePropertyChange(PROPERTY_NAME_SHOW_TODAY_BTN, old, new Boolean( | |
348 | visible)); | |
349 | } | |
350 | ||
351 | /** | |
352 | * Is the none-button visible? | |
353 | * <p> | |
354 | * The none-button allows the user to select empty date (null-date, no | |
355 | * date). | |
356 | * | |
357 | * @return <code>true</code> if the none-button is visible, | |
358 | * <code>false</code> otherwise | |
359 | */ | |
360 | public boolean isShowNoneButton() { | |
361 | return showNoneButton; | |
362 | } | |
363 | ||
364 | /** | |
365 | * Shows or hides the none-button. | |
366 | * <p> | |
367 | * The none-button allows the user to select empty date (null-date, no | |
368 | * date). | |
369 | * | |
370 | * @param visible | |
371 | * <code>true</code> to show the none-button <code>false</code> | |
372 | * to hide | |
373 | */ | |
374 | public void setShowNoneButton(boolean visible) { | |
375 | Boolean old = new Boolean(this.showNoneButton); | |
376 | this.showNoneButton = visible; | |
377 | firePropertyChange(PROPERTY_NAME_SHOW_NONE_BTN, old, new Boolean( | |
378 | visible)); | |
379 | } | |
380 | ||
381 | /** | |
382 | * Returns the focus lost behavior. Possible values are: | |
383 | * | |
384 | * <ul> | |
385 | * <li><code> {@link JFormattedTextField#COMMIT}</code> <li><code> | |
386 | * {@link JFormattedTextField#COMMIT_OR_REVERT}</code> <li><code> | |
387 | * {@link JFormattedTextField#REVERT}</code> <li><code> | |
388 | * {@link JFormattedTextField#PERSIST}</code> | |
389 | * </ul> | |
390 | * Original meaning preserved. | |
391 | * | |
392 | * @return the focus lost behavior constant | |
393 | * @see JFormattedTextField | |
394 | */ | |
395 | public int getFocusLostBehavior() { | |
396 | return focusLostBehavior; | |
397 | } | |
398 | ||
399 | /** | |
400 | * Sets the focus lost behaviour. Possible values are: | |
401 | * | |
402 | * <ul> | |
403 | * <li><code> {@link JFormattedTextField#COMMIT}</code> <li><code> | |
404 | * {@link JFormattedTextField#COMMIT_OR_REVERT}</code> <li><code> | |
405 | * {@link JFormattedTextField#REVERT}</code> <li><code> | |
406 | * {@link JFormattedTextField#PERSIST}</code> | |
407 | * </ul> | |
408 | * Original meaning preserved. | |
409 | * | |
410 | * @param behavior | |
411 | * the focus lost behavior constant | |
412 | * @see JFormattedTextField | |
413 | */ | |
414 | public void setFocusLostBehavior(int behavior) { | |
415 | behavior = checkFocusLostbehavior(behavior); | |
416 | int old = this.focusLostBehavior; | |
417 | this.focusLostBehavior = behavior; | |
418 | firePropertyChange(PROPERTY_NAME_FOCUS_LOST_BEHAVIOR, old, behavior); | |
419 | } | |
420 | ||
421 | /** | |
422 | * Resurns current calendar resources model. | |
423 | * <p> | |
424 | * The model is used to query localized resources for the control. | |
425 | * | |
426 | * @return current calendar resources model | |
427 | * @see CalendarResources | |
428 | */ | |
429 | public CalendarResources getResources() { | |
430 | return resources; | |
431 | } | |
432 | ||
433 | /** | |
434 | * Sets current calendar resources model. | |
435 | * <p> | |
436 | * The model is used to query localized resources for the control. | |
437 | * | |
438 | * @param resources | |
439 | * a calendar resources model to set. Should not be | |
440 | * <code>null</code> | |
441 | * @see CalendarResources | |
442 | */ | |
443 | public void setResources(CalendarResources resources) { | |
444 | CalendarResources old = this.resources; | |
445 | this.resources = resources; | |
446 | firePropertyChange(PROPERTY_NAME_RESOURCES, old, resources); | |
447 | } | |
448 | ||
449 | /** | |
450 | * Returns current holliday policy (model). | |
451 | * <p> | |
452 | * The policy is used to query holliday dates and holliday descriptions. | |
453 | * | |
454 | * @return current holliday policy or <code>null</code> if none set | |
455 | * @see HolidayPolicy | |
456 | */ | |
457 | public HolidayPolicy getHolidayPolicy() { | |
458 | return holidayPolicy; | |
459 | } | |
460 | ||
461 | /** | |
462 | * Sets current holliday policy (model) then updates the control to reflect | |
463 | * the policy set. | |
464 | * <p> | |
465 | * The policy is used to query holliday dates and holiday descriptions. | |
466 | * | |
467 | * @param holidayPolicy | |
468 | * a holliday policy to set. May be <code>null</code> | |
469 | * @see VetoPolicy | |
470 | */ | |
471 | public void setHolidayPolicy(HolidayPolicy holidayPolicy) { | |
472 | HolidayPolicy old = this.holidayPolicy; | |
473 | this.holidayPolicy = holidayPolicy; | |
474 | firePropertyChange(PROPERTY_NAME_HOLIDAY_POLICY, old, holidayPolicy); | |
475 | } | |
476 | ||
477 | /** | |
478 | * Returns the current veto policy (model). | |
479 | * <p> | |
480 | * The policy is used to veto dates in the control. | |
481 | * | |
482 | * @return current veto policy or <code>null</code> if none set | |
483 | * @see VetoPolicy | |
484 | */ | |
485 | public VetoPolicy getVetoPolicy() { | |
486 | return vetoPolicy; | |
487 | } | |
488 | ||
489 | /** | |
490 | * Sets the current veto policy (model). | |
491 | * <p> | |
492 | * The policy is used to veto dates in the control. | |
493 | * | |
494 | * @param vetoModel | |
495 | * a veto policy to set. May be <code>null</code> | |
496 | */ | |
497 | public void setVetoPolicy(VetoPolicy vetoModel) { | |
498 | VetoPolicy old = this.vetoPolicy; | |
499 | this.vetoPolicy = vetoModel; | |
500 | firePropertyChange(PROPERTY_NAME_VETO_POLICY, old, vetoModel); | |
501 | } | |
502 | ||
503 | /** | |
504 | * Is the number of every week visible? | |
505 | * | |
506 | * @return <code>true</code> if the number of every week is visible, | |
507 | * <code>false</code> otherwise | |
508 | */ | |
509 | public boolean isShowNumberOfWeek() { | |
510 | return showNumberOfWeek; | |
511 | } | |
512 | ||
513 | /** | |
514 | * Is time protion of the date automatically striped, based on current | |
515 | * locale and ime zone? | |
516 | * | |
517 | * @return <code>true</code> if {@link #getDate()} returns a stripped date, | |
518 | * <code>false</code> otherwise | |
519 | * @see #setStripTime(boolean) | |
520 | * @see #stripTime(Date, TimeZone, Locale) | |
521 | */ | |
522 | public boolean isStripTime() { | |
523 | return stripTime; | |
524 | } | |
525 | ||
526 | /** | |
527 | * Makes {@link #getDate()} either strip the time portion of the date, or | |
528 | * keep it. | |
529 | * | |
530 | * @param stripTime | |
531 | * <code>true</code> to strip time, <code>false</code> to keep | |
532 | * time | |
533 | */ | |
534 | public void setStripTime(boolean stripTime) { | |
535 | this.stripTime = stripTime; | |
536 | } | |
537 | ||
538 | /** | |
539 | * Shows or hides the the number of every week. | |
540 | * <p> | |
541 | * The number of week is based on the current locale for the component. | |
542 | * | |
543 | * @param visible | |
544 | * <code>true</code> to show the the number of every week | |
545 | * <code>false</code> to hide | |
546 | */ | |
547 | public void setShowNumberOfWeek(boolean visible) { | |
548 | boolean old = this.showNumberOfWeek; | |
549 | this.showNumberOfWeek = visible; | |
550 | firePropertyChange(PROPERTY_NAME_SHOW_NUMBER_WEEK, old, visible); | |
551 | } | |
552 | ||
553 | /** | |
554 | * Adds an {@link ActionListener} listener. | |
555 | * | |
556 | * @param listener | |
557 | * a listener to add | |
558 | * @see ActionListener | |
559 | */ | |
560 | public void addActionListener(ActionListener listener) { | |
561 | actionListenerList.add(ActionListener.class, listener); | |
562 | } | |
563 | ||
564 | /** | |
565 | * Removes an {@link ActionListener} listener. | |
566 | * | |
567 | * @param listener | |
568 | * a listener to remove | |
569 | * @see ActionListener | |
570 | */ | |
571 | public void removeActionListener(ActionListener listener) { | |
572 | actionListenerList.remove(ActionListener.class, listener); | |
573 | ||
574 | } | |
575 | ||
576 | /** | |
577 | * Adds an {@link CommitListener} listener. | |
578 | * | |
579 | * @param listener | |
580 | * a listener to add | |
581 | * @see CommitListener | |
582 | */ | |
583 | public void addCommitListener(CommitListener listener) { | |
584 | commitListenerList.add(CommitListener.class, listener); | |
585 | } | |
586 | ||
587 | /** | |
588 | * Removes an {@link CommitListener} listener. | |
589 | * | |
590 | * @param listener | |
591 | * a listener to remove | |
592 | * @see CommitListener | |
593 | */ | |
594 | public void removeCommitListener(CommitListener listener) { | |
595 | commitListenerList.remove(CommitListener.class, listener); | |
596 | } | |
597 | ||
598 | /** | |
599 | * Forces the control to commit current user's edit. The opertaion may fail | |
600 | * because the date in the control may be restricted by current veto policy. | |
601 | * If successfull, the current date of the control may change, a | |
602 | * {@link CommitEvent} is fired. | |
603 | * | |
604 | * @return <code>true</code> if successful, <code>false</code> otherwise | |
605 | * @see #revertEdit() | |
606 | * @see #getFocusLostBehavior() | |
607 | * @see #setFocusLostBehavior(int) | |
608 | */ | |
609 | public boolean commitEdit() { | |
610 | try { | |
611 | ((CalendarPaneUI) getUI()).commit(); | |
612 | fireCommitEvent(true); | |
613 | return true; | |
614 | } catch (Exception e) { | |
615 | return false; | |
616 | } | |
617 | } | |
618 | ||
619 | /** | |
620 | * Forces the control to revert current user's edit to reflect current | |
621 | * control's date. The current date of the control may change, a | |
622 | * {@link CommitEvent} is fired. | |
623 | * | |
624 | * @see #revertEdit() | |
625 | * @see #getFocusLostBehavior() | |
626 | * @see #setFocusLostBehavior(int) | |
627 | */ | |
628 | public void revertEdit() { | |
629 | ((CalendarPaneUI) getUI()).revert(); | |
630 | fireCommitEvent(false); | |
631 | } | |
632 | ||
633 | /** | |
634 | * Forces the control to commit or revert user's edit depending on the | |
635 | * current focus lost behavior as if the focus would be lost. | |
636 | * | |
637 | * @see #commitEdit() | |
638 | * @see #revertEdit() | |
639 | * @see #getFocusLostBehavior() | |
640 | * @see #setFocusLostBehavior(int) | |
641 | */ | |
642 | public void commitOrRevert() { | |
643 | switch (focusLostBehavior) { | |
644 | case JFormattedTextField.REVERT: | |
645 | revertEdit(); | |
646 | break; | |
647 | case JFormattedTextField.COMMIT: | |
648 | commitEdit(); | |
649 | break; | |
650 | case JFormattedTextField.COMMIT_OR_REVERT: | |
651 | if (!commitEdit()) | |
652 | revertEdit(); | |
653 | break; | |
654 | case JFormattedTextField.PERSIST: | |
655 | // do nothing | |
656 | break; | |
657 | } | |
658 | } | |
659 | ||
660 | /** | |
661 | * Fires a {@link CommitEvent} to all registered listeners. | |
662 | * | |
663 | * @param commit | |
664 | * <code>true</code> to indicate commit, <code>false</code> to | |
665 | * indicate revert | |
666 | * @see CommitEvent | |
667 | * @see CommitListener | |
668 | */ | |
669 | public void fireCommitEvent(boolean commit) { | |
670 | Object[] listeners = commitListenerList.getListenerList(); | |
671 | ||
672 | for (int i = listeners.length - 2; i >= 0; i -= 2) | |
673 | if (listeners[i] == CommitListener.class) | |
674 | ((CommitListener) listeners[i + 1]).commit(new CommitEvent( | |
675 | this, commit)); | |
676 | } | |
677 | ||
678 | /** | |
679 | * Fires a {@link ActionEvent} to all registered listeners. | |
680 | * | |
681 | * @see ActionEvent | |
682 | * @see ActionListener | |
683 | */ | |
684 | public void fireActionEvent() { | |
685 | Object[] listeners = actionListenerList.getListenerList(); | |
686 | ||
687 | for (int i = listeners.length - 2; i >= 0; i -= 2) | |
688 | if (listeners[i] == ActionListener.class) | |
689 | ((ActionListener) listeners[i + 1]) | |
690 | .actionPerformed(new ActionEvent(this, 0, "value")); | |
691 | } | |
692 | ||
693 | private void checkTimeZone(TimeZone zone) { | |
694 | if (zone == null) | |
695 | throw new IllegalArgumentException("'zone' can not be null."); | |
696 | ||
697 | } | |
698 | ||
699 | private void checkLocale(Locale locale) { | |
700 | if (locale == null) | |
701 | throw new IllegalArgumentException("'locale' can not be null."); | |
702 | ||
703 | } | |
704 | ||
705 | private int checkFocusLostbehavior(int behavior) { | |
706 | if (behavior != JFormattedTextField.COMMIT | |
707 | && behavior != JFormattedTextField.COMMIT_OR_REVERT | |
708 | && behavior != JFormattedTextField.REVERT | |
709 | && behavior != JFormattedTextField.PERSIST) | |
710 | throw new IllegalArgumentException( | |
711 | PROPERTY_NAME_FOCUS_LOST_BEHAVIOR | |
712 | + ": unrecognized behavior"); | |
713 | return behavior; | |
714 | } | |
715 | ||
716 | private boolean checkDate(Date date) { | |
717 | if (vetoPolicy != null) { | |
718 | if (date == null) | |
719 | return !vetoPolicy.isRestrictNull(this); | |
720 | return !vetoPolicy.isRestricted(this, makeCurrentCalendar(date)); | |
721 | } else | |
722 | return true; | |
723 | } | |
724 | ||
725 | private int checkStyle(int style) { | |
726 | if (style == 0) | |
727 | style = STYLE_CLASSIC; | |
728 | if (style != STYLE_CLASSIC && style != STYLE_MODERN) | |
729 | throw new IllegalArgumentException(PROPERTY_NAME_STYLE | |
730 | + ": unrecognized style"); | |
731 | return style; | |
732 | } | |
733 | ||
734 | private Calendar makeCurrentCalendar(Date date) { | |
735 | Calendar c = Calendar.getInstance(zone, locale); | |
736 | c.setTime(date); | |
737 | return c; | |
738 | } | |
739 | ||
740 | /** | |
741 | * Returns same date as given, but time portion (hours, minutes, seconds, | |
742 | * fraction of second) set to zero, based on given locale and time zone. | |
743 | * Utility method. | |
744 | * <p> | |
745 | * Examle:<br> | |
746 | * Fri Sep 29 15:57:23 EEST 2006 -> Fri Sep 29 00:00:00 EEST 2006 | |
747 | * | |
748 | * @param date | |
749 | * date to strip time from | |
750 | * @param zone | |
751 | * time zone to get zero fields in | |
752 | * @param locale | |
753 | * locale to base the calendar on | |
754 | * @return stripped date | |
755 | */ | |
756 | public static Date stripTime(Date date, TimeZone zone, Locale locale) { | |
757 | if (date == null) | |
758 | return null; | |
759 | Calendar tmpCalendar = Calendar.getInstance(zone, locale); | |
760 | tmpCalendar.setTime(date); | |
761 | tmpCalendar.set(Calendar.HOUR_OF_DAY, tmpCalendar | |
762 | .getMinimum(Calendar.HOUR_OF_DAY)); | |
763 | tmpCalendar.set(Calendar.MINUTE, tmpCalendar | |
764 | .getMinimum(Calendar.MINUTE)); | |
765 | tmpCalendar.set(Calendar.SECOND, tmpCalendar | |
766 | .getMinimum(Calendar.SECOND)); | |
767 | tmpCalendar.set(Calendar.MILLISECOND, tmpCalendar | |
768 | .getMinimum(Calendar.MILLISECOND)); | |
769 | return tmpCalendar.getTime(); | |
770 | } | |
771 | ||
772 | /** | |
773 | * Makes the widget display given month within given year without actually | |
774 | * changing selected date. | |
775 | * | |
776 | * @param year | |
777 | * year to show | |
778 | * @param month | |
779 | * month within the year to show | |
780 | */ | |
781 | public void observeMonth(int year, int month) { | |
782 | ((CalendarPaneUI) getUI()).observeMonth(year, month); | |
783 | } | |
784 | ||
785 | } |
0 | package com.michaelbaranov.microba.calendar; | |
1 | ||
2 | import java.util.Locale; | |
3 | ||
4 | /** | |
5 | * An interface is used to provide localized string resources for | |
6 | * {@link CalendarPane} and {@link DatePicker} classes. | |
7 | * | |
8 | * @author Michael Baranov | |
9 | * | |
10 | */ | |
11 | public interface CalendarResources { | |
12 | ||
13 | /** | |
14 | * A key for "today" word | |
15 | */ | |
16 | public static final String KEY_TODAY = "key.today"; | |
17 | ||
18 | /** | |
19 | * A key for "none" word | |
20 | */ | |
21 | public static final String KEY_NONE = "key.none"; | |
22 | ||
23 | /** | |
24 | * This method is used to query tring resources for {@link CalendarPane} and | |
25 | * {@link DatePicker} classes. Should not return <code>null</code>. | |
26 | * | |
27 | * @param key | |
28 | * one of the keys defined by {@link CalendarResources} | |
29 | * @param locale | |
30 | * a {@link Locale} | |
31 | * @return localized string resource for a given key | |
32 | */ | |
33 | public String getResource(String key, Locale locale); | |
34 | } |
0 | package com.michaelbaranov.microba.calendar; | |
1 | ||
2 | import java.text.DateFormat; | |
3 | import java.util.Date; | |
4 | import java.util.Locale; | |
5 | import java.util.Map; | |
6 | import java.util.TimeZone; | |
7 | ||
8 | import com.michaelbaranov.microba.calendar.ui.DatePickerUI; | |
9 | ||
10 | /** | |
11 | * A concrete implementation of JComponent. Capable of displaying and selecting | |
12 | * dates, much like an editable combo-box with a calendar dropdown. | |
13 | * <p> | |
14 | * This implementatin allows for specifying time component along with the date. | |
15 | * Make sure that: 1) keepTime property is true; 2) stripTime is false; 3) | |
16 | * dateFormat has time fields; | |
17 | * | |
18 | * @author Michael Baranov | |
19 | */ | |
20 | public class DatePicker extends CalendarPane { | |
21 | ||
22 | /** | |
23 | * The name of a "dateFormat" property. | |
24 | */ | |
25 | public static final String PROPERTY_NAME_DATE_FORMAT = "dateFormat"; | |
26 | ||
27 | /** | |
28 | * The name of a "fieldEditable" property. | |
29 | */ | |
30 | public static final String PROPERTY_NAME_FIELD_EDITABLE = "fieldEditable"; | |
31 | ||
32 | /** | |
33 | * The name of a "keepTime" property. | |
34 | */ | |
35 | public static final String PROPERTY_NAME_KEEP_TIME = "keepTime"; | |
36 | ||
37 | /** | |
38 | * The name of a "pickerStyle" property. | |
39 | */ | |
40 | public static final String PROPERTY_NAME_PICKER_STYLE = "pickerStyle"; | |
41 | ||
42 | /** | |
43 | * The name of a "popupFocusable" property. | |
44 | */ | |
45 | public static final String PROPERTY_NAME_DROPDOWN_FOCUSABLE = "dropdownFocusable"; | |
46 | ||
47 | /** | |
48 | * A constant for the "pickerStyle" property. | |
49 | */ | |
50 | public static final int PICKER_STYLE_FIELD_AND_BUTTON = 0x110; | |
51 | ||
52 | /** | |
53 | * A constant for the "pickerStyle" property. | |
54 | */ | |
55 | public static final int PICKER_STYLE_BUTTON = 0x120; | |
56 | ||
57 | private static final String uiClassID = "microba.DatePickerUI"; | |
58 | ||
59 | private DateFormat dateFormat; | |
60 | ||
61 | private boolean fieldEditable; | |
62 | ||
63 | private boolean keepTime; | |
64 | ||
65 | private int pickerStyle; | |
66 | ||
67 | private boolean dropdownFocusable; | |
68 | ||
69 | /** | |
70 | * Constructor. | |
71 | */ | |
72 | public DatePicker() { | |
73 | this(new Date(), DateFormat.MEDIUM, Locale.getDefault(), TimeZone | |
74 | .getDefault()); | |
75 | } | |
76 | ||
77 | /** | |
78 | * Constructor. | |
79 | */ | |
80 | public DatePicker(Date initialDate) { | |
81 | this(initialDate, DateFormat.MEDIUM, Locale.getDefault(), TimeZone | |
82 | .getDefault()); | |
83 | } | |
84 | ||
85 | /** | |
86 | * Constructor. | |
87 | */ | |
88 | public DatePicker(Date initialDate, int dateStyle) { | |
89 | this(initialDate, dateStyle, Locale.getDefault(), TimeZone.getDefault()); | |
90 | } | |
91 | ||
92 | /** | |
93 | * Constructor. | |
94 | */ | |
95 | public DatePicker(Date initialDate, DateFormat dateFormat) { | |
96 | this(initialDate, dateFormat, Locale.getDefault(), TimeZone | |
97 | .getDefault()); | |
98 | } | |
99 | ||
100 | /** | |
101 | * Constructor. | |
102 | */ | |
103 | public DatePicker(Date initialDate, int dateStyle, Locale locale) { | |
104 | this(initialDate, dateStyle, locale, TimeZone.getDefault()); | |
105 | } | |
106 | ||
107 | /** | |
108 | * Constructor. | |
109 | */ | |
110 | public DatePicker(Date initialDate, DateFormat dateFormat, Locale locale) { | |
111 | this(initialDate, dateFormat, locale, TimeZone.getDefault()); | |
112 | } | |
113 | ||
114 | /** | |
115 | * Constructor. | |
116 | */ | |
117 | public DatePicker(Date initialDate, int dateStyle, Locale locale, | |
118 | TimeZone zone) { | |
119 | super(initialDate, CalendarPane.STYLE_CLASSIC, locale, zone); | |
120 | checkDateStyle(dateStyle); | |
121 | this.dateFormat = dateFormatFromStyle(dateStyle); | |
122 | this.fieldEditable = true; | |
123 | this.keepTime = true; | |
124 | this.pickerStyle = PICKER_STYLE_FIELD_AND_BUTTON; | |
125 | this.setStripTime(false); | |
126 | this.dropdownFocusable = true; | |
127 | updateUI(); | |
128 | } | |
129 | ||
130 | /** | |
131 | * Constructor. | |
132 | */ | |
133 | public DatePicker(Date initialDate, DateFormat dateFormat, Locale locale, | |
134 | TimeZone zone) { | |
135 | super(initialDate, CalendarPane.STYLE_CLASSIC, locale, zone); | |
136 | checkDateFormat(dateFormat); | |
137 | this.dateFormat = dateFormat; | |
138 | this.fieldEditable = true; | |
139 | this.keepTime = true; | |
140 | this.pickerStyle = PICKER_STYLE_FIELD_AND_BUTTON; | |
141 | this.setStripTime(false); | |
142 | this.dropdownFocusable = true; | |
143 | updateUI(); | |
144 | } | |
145 | ||
146 | public String getUIClassID() { | |
147 | return uiClassID; | |
148 | } | |
149 | ||
150 | /** | |
151 | * Returns the date format. | |
152 | * <p> | |
153 | * | |
154 | * @return current date format | |
155 | * @see #setDateFormat(DateFormat) | |
156 | */ | |
157 | public DateFormat getDateFormat() { | |
158 | return dateFormat; | |
159 | } | |
160 | ||
161 | /** | |
162 | * Sets the date format constant defined by {@link DateFormat} and updates | |
163 | * the control to reflect new date style. | |
164 | * <p> | |
165 | * | |
166 | * @param dateFormat | |
167 | * the date format constant to set | |
168 | * @see #getDateFormat() | |
169 | * @see DateFormat | |
170 | */ | |
171 | public void setDateFormat(DateFormat dateFormat) { | |
172 | checkDateFormat(dateFormat); | |
173 | Object oldValue = this.dateFormat; | |
174 | this.dateFormat = dateFormat; | |
175 | firePropertyChange(PROPERTY_NAME_DATE_FORMAT, oldValue, dateFormat); | |
176 | } | |
177 | ||
178 | /** | |
179 | * Is the edit field of the control editable by the user? | |
180 | * <p> | |
181 | * If not editable, the user can not type in the date and can only use | |
182 | * calendar drop-down to select dates. | |
183 | * | |
184 | * @return <code>true</code> if the edit field is editable, | |
185 | * <code>false</code> otherwise | |
186 | * | |
187 | * @see #setFieldEditable(boolean) | |
188 | */ | |
189 | public boolean isFieldEditable() { | |
190 | return fieldEditable; | |
191 | } | |
192 | ||
193 | /** | |
194 | * Enables or disables editing of the edit field by the user. | |
195 | * <p> | |
196 | * If not editable, the user can not type in the date and can only use | |
197 | * calendar drop-down to select dates. | |
198 | * | |
199 | * @param fieldEditable | |
200 | * the editable value to set | |
201 | * | |
202 | * @see #isFieldEditable() | |
203 | */ | |
204 | public void setFieldEditable(boolean fieldEditable) { | |
205 | boolean old = this.fieldEditable; | |
206 | this.fieldEditable = fieldEditable; | |
207 | firePropertyChange(PROPERTY_NAME_FIELD_EDITABLE, old, fieldEditable); | |
208 | } | |
209 | ||
210 | /** | |
211 | * Is the dropdown focusable? | |
212 | * <p> | |
213 | * If not focusable, the dropdown calendar will lack some keyboard input | |
214 | * capabilities. | |
215 | * | |
216 | * @return <code>true</code> if the dropdown is focusable, | |
217 | * <code>false</code> otherwise | |
218 | * | |
219 | * @see #setDropdownFocusable(boolean) | |
220 | */ | |
221 | public boolean isDropdownFocusable() { | |
222 | return dropdownFocusable; | |
223 | } | |
224 | ||
225 | /** | |
226 | * Enables or disables focusability of the dropdown calendar. | |
227 | * <p> | |
228 | * If not focusable, the dropdown calendar will lack some keyboard input | |
229 | * capabilities. | |
230 | * | |
231 | * @param popupFocusable | |
232 | * the focusable value to set | |
233 | * | |
234 | * @see #isDropdownFocusable() | |
235 | */ | |
236 | public void setDropdownFocusable(boolean popupFocusable) { | |
237 | boolean old = this.dropdownFocusable; | |
238 | this.dropdownFocusable = popupFocusable; | |
239 | firePropertyChange(PROPERTY_NAME_DROPDOWN_FOCUSABLE, old, | |
240 | popupFocusable); | |
241 | } | |
242 | ||
243 | /** | |
244 | * Does UI try to preserve time components entered in the edit field? | |
245 | * <p> | |
246 | * If <code>true</code> and if the date format has some time fields | |
247 | * (hours, minutes, seconds, fraction of second), the UI tries to respect | |
248 | * the time fields' values entered by user as much as possible. | |
249 | * <p> | |
250 | * Note: to be able to receive time portion of the date, make sure | |
251 | * {@link #isStripTime()} is <code>false</code> (the dafualt). | |
252 | * | |
253 | * @return <code>true</code> if the UI respects time fields, | |
254 | * <code>false</code> otherwise | |
255 | * @see #setKeepTime(boolean) | |
256 | * @see #setStripTime(boolean) | |
257 | * @see #isStripTime() | |
258 | * | |
259 | */ | |
260 | public boolean isKeepTime() { | |
261 | return keepTime; | |
262 | } | |
263 | ||
264 | /** | |
265 | * Determines if the UI should try to preserve time components entered in | |
266 | * the edit field. | |
267 | * <p> | |
268 | * If <code>true</code> and if the date format has some time fields | |
269 | * (hours, minutes, seconds, fraction of second), the UI tries to respect | |
270 | * the time fields' values entered by user as much as possible. | |
271 | * <p> | |
272 | * Note: to be able to receive time portion of the date, make sure | |
273 | * {@link #isStripTime()} is <code>false</code> (the dafualt). | |
274 | * | |
275 | * @param keepTime | |
276 | * <code>true</code> to make the UI respects time fields, | |
277 | * <code>false</code> otherwise | |
278 | * @see #isKeepTime() | |
279 | * @see #setStripTime(boolean) | |
280 | * @see #isStripTime() | |
281 | */ | |
282 | public void setKeepTime(boolean keepTime) { | |
283 | boolean old = this.keepTime; | |
284 | this.keepTime = keepTime; | |
285 | firePropertyChange(PROPERTY_NAME_KEEP_TIME, old, keepTime); | |
286 | } | |
287 | ||
288 | /** | |
289 | * Returns current visual style of the picker control. | |
290 | * <p> | |
291 | * NOTE: do not confuse with {@link #getStyle()}. | |
292 | * | |
293 | * @return current visual style constant. | |
294 | */ | |
295 | public int getPickerStyle() { | |
296 | return pickerStyle; | |
297 | } | |
298 | ||
299 | /** | |
300 | * Sets the current visual style of the picker control. | |
301 | * <p> | |
302 | * The control is then updated to reflect the new style. | |
303 | * <p> | |
304 | * NOTE: do not confuse with {@link #getStyle()}. | |
305 | * | |
306 | * @param pickerStyle | |
307 | * the style to set | |
308 | * @see #PICKER_STYLE_BUTTON | |
309 | * @see #PICKER_STYLE_FIELD_AND_BUTTON | |
310 | */ | |
311 | public void setPickerStyle(int pickerStyle) { | |
312 | pickerStyle = checkPickerStyle(pickerStyle); | |
313 | int oldValue = this.pickerStyle; | |
314 | this.pickerStyle = pickerStyle; | |
315 | firePropertyChange(PROPERTY_NAME_PICKER_STYLE, oldValue, pickerStyle); | |
316 | } | |
317 | ||
318 | /** | |
319 | * A shortucut method to switch picker style between | |
320 | * {@link #PICKER_STYLE_FIELD_AND_BUTTON} and {@link #PICKER_STYLE_BUTTON} | |
321 | * | |
322 | * @param buttonOnly | |
323 | * <code>true</code> to set {@link #PICKER_STYLE_BUTTON}, | |
324 | * <code>false</code> to set | |
325 | * {@link #PICKER_STYLE_FIELD_AND_BUTTON} | |
326 | */ | |
327 | public void showButtonOnly(boolean buttonOnly) { | |
328 | if (buttonOnly) | |
329 | setPickerStyle(PICKER_STYLE_BUTTON); | |
330 | else | |
331 | setPickerStyle(PICKER_STYLE_FIELD_AND_BUTTON); | |
332 | } | |
333 | ||
334 | /** | |
335 | * Displays the calendar dropdown. | |
336 | */ | |
337 | public void showPopup() { | |
338 | ((DatePickerUI) getUI()).showPopup(true); | |
339 | } | |
340 | ||
341 | /** | |
342 | * Hides the calendar dropdown without selecting a date. | |
343 | */ | |
344 | public void hidePopup() { | |
345 | ((DatePickerUI) getUI()).showPopup(false); | |
346 | } | |
347 | ||
348 | public boolean commitEdit() { | |
349 | try { | |
350 | ((DatePickerUI) getUI()).commit(); | |
351 | fireCommitEvent(true); | |
352 | return true; | |
353 | } catch (Exception e) { | |
354 | return false; | |
355 | } | |
356 | } | |
357 | ||
358 | public void revertEdit() { | |
359 | ((DatePickerUI) getUI()).revert(); | |
360 | fireCommitEvent(false); | |
361 | } | |
362 | ||
363 | private void checkDateStyle(int style) { | |
364 | if (style != DateFormat.SHORT && style != DateFormat.MEDIUM | |
365 | && style != DateFormat.LONG) | |
366 | throw new IllegalArgumentException("dateStyle: unrecognized style"); | |
367 | } | |
368 | ||
369 | private int checkPickerStyle(int style) { | |
370 | if (style == 0) | |
371 | style = PICKER_STYLE_FIELD_AND_BUTTON; | |
372 | if (style != PICKER_STYLE_FIELD_AND_BUTTON | |
373 | && style != PICKER_STYLE_BUTTON) | |
374 | throw new IllegalArgumentException(PROPERTY_NAME_PICKER_STYLE | |
375 | + ": unrecognized style"); | |
376 | return style; | |
377 | } | |
378 | ||
379 | private void checkDateFormat(DateFormat dateFormat) { | |
380 | if (dateFormat == null) | |
381 | throw new IllegalArgumentException("dateFormat: null value"); | |
382 | } | |
383 | ||
384 | private DateFormat dateFormatFromStyle(int dateStyle) { | |
385 | DateFormat df = DateFormat.getDateInstance(dateStyle, this.getLocale()); | |
386 | df.setTimeZone(this.getZone()); | |
387 | return df; | |
388 | } | |
389 | ||
390 | } |
0 | package com.michaelbaranov.microba.calendar; | |
1 | ||
2 | import java.beans.PropertyVetoException; | |
3 | import java.util.Date; | |
4 | ||
5 | import javax.swing.DefaultCellEditor; | |
6 | import javax.swing.JCheckBox; | |
7 | import javax.swing.table.TableCellEditor; | |
8 | import javax.swing.tree.TreeCellEditor; | |
9 | ||
10 | import com.michaelbaranov.microba.calendar.ui.DatePickerUI; | |
11 | ||
12 | /** | |
13 | * This class in a concrete implementation of {@link TableCellEditor} and | |
14 | * {@link TreeCellEditor} interfaces. Uses {@link DatePicker} control as en | |
15 | * editor. Subclass to extend functionality. * | |
16 | * <p> | |
17 | * Note: you probably will want to set the property of the {@link DatePicker} | |
18 | * {@value DatePicker#PROPERTY_NAME_DROPDOWN_FOCUSABLE} to <code>false</code> | |
19 | * before using it to construct {@link DatePickerCellEditor}. | |
20 | * | |
21 | * @see DefaultCellEditor | |
22 | * | |
23 | * @author Michael Baranov | |
24 | * | |
25 | */ | |
26 | public class DatePickerCellEditor extends DefaultCellEditor { | |
27 | ||
28 | /** | |
29 | * Constructor. | |
30 | * <p> | |
31 | * Note: you probably will want to set the property of the | |
32 | * {@link DatePicker} {@value DatePicker#PROPERTY_NAME_DROPDOWN_FOCUSABLE} | |
33 | * to <code>false</code> before using it to construct | |
34 | * {@link DatePickerCellEditor}. | |
35 | * | |
36 | * @param datePicker | |
37 | * the editor component | |
38 | */ | |
39 | public DatePickerCellEditor(final DatePicker datePicker) { | |
40 | // trick: supply a dummy JCheckBox | |
41 | super(new JCheckBox()); | |
42 | // get back the dummy JCheckBox | |
43 | JCheckBox checkBox = (JCheckBox) this.editorComponent; | |
44 | // remove listeners installed by super() | |
45 | checkBox.removeActionListener(this.delegate); | |
46 | // replace editor component with own | |
47 | this.editorComponent = datePicker; | |
48 | ||
49 | // set simple look | |
50 | ((DatePickerUI) datePicker.getUI()).setSimpeLook(true); | |
51 | ||
52 | // replace delegate with own | |
53 | this.delegate = new EditorDelegate() { | |
54 | public void setValue(Object value) { | |
55 | try { | |
56 | ((DatePicker) editorComponent).setDate((Date) value); | |
57 | } catch (PropertyVetoException e) { | |
58 | } | |
59 | } | |
60 | ||
61 | public Object getCellEditorValue() { | |
62 | return ((DatePicker) editorComponent).getDate(); | |
63 | } | |
64 | ||
65 | public void cancelCellEditing() { | |
66 | ((DatePicker) editorComponent).commitOrRevert(); | |
67 | super.cancelCellEditing(); | |
68 | } | |
69 | ||
70 | public boolean stopCellEditing() { | |
71 | ((DatePicker) editorComponent).commitOrRevert(); | |
72 | return super.stopCellEditing(); | |
73 | } | |
74 | ||
75 | }; | |
76 | // install listeners | |
77 | datePicker.addActionListener(delegate); | |
78 | // do not set it to 1 | |
79 | setClickCountToStart(2); | |
80 | } | |
81 | ||
82 | }⏎ |
0 | package com.michaelbaranov.microba.calendar; | |
1 | ||
2 | import java.io.IOException; | |
3 | import java.util.Locale; | |
4 | import java.util.Properties; | |
5 | ||
6 | /** | |
7 | * A very basic implementation of {@link CalendarResources}. Used by default by | |
8 | * {@link CalendarPane} and {@link DatePicker} classes. The resources are loaded | |
9 | * from 'DefaultCalendarResources.properties' file. | |
10 | * | |
11 | * @author Michael Baranov | |
12 | * | |
13 | */ | |
14 | public class DefaultCalendarResources implements CalendarResources { | |
15 | ||
16 | private static final String RESOURCE_FILE = "DefaultCalendarResources.properties"; | |
17 | ||
18 | private static final String DEFAULT_LANGUAGE = "en"; | |
19 | ||
20 | private Properties strings = new Properties(); | |
21 | ||
22 | /** | |
23 | * Constructor. | |
24 | */ | |
25 | public DefaultCalendarResources() { | |
26 | try { | |
27 | strings.load(DefaultCalendarResources.class | |
28 | .getResourceAsStream(RESOURCE_FILE)); | |
29 | } catch (IOException e) { | |
30 | e.printStackTrace(); | |
31 | } | |
32 | } | |
33 | ||
34 | public String getResource(String key, Locale locale) { | |
35 | String language = locale.getLanguage(); | |
36 | String word = (String) strings.get(language + "." + key); | |
37 | if (word == null) | |
38 | word = (String) strings.get(DEFAULT_LANGUAGE + "." + key); | |
39 | return word; | |
40 | } | |
41 | ||
42 | } |
+36
-0
0 | en.key.none=none | |
1 | en.key.today=today | |
2 | de.key.none=kein | |
3 | de.key.today=heute | |
4 | fr.key.none=non | |
5 | fr.key.today=aujourd'hui | |
6 | pt.key.none=limpar | |
7 | pt.key.today=hoje | |
8 | ru.key.none=\u043D\u0435\u0442 | |
9 | ru.key.today=\u0441\u0435\u0433\u043E\u0434\u043D\u044F | |
10 | it.key.none=nessuno | |
11 | it.key.today=oggi | |
12 | nl.key.none=geen | |
13 | nl.key.today=vandaag | |
14 | es.key.none=ninguna | |
15 | es.key.today=hoy | |
16 | pl.key.none=\u017Caden | |
17 | pl.key.today=dzisiaj | |
18 | by.key.none=\u043Di\u0447\u043E\u0433\u0430 | |
19 | by.key.today=\u0441\u0451\u043D\u043D\u044F | |
20 | da.key.none=ingen | |
21 | da.key.today=i dag | |
22 | ro.key.none=sterge | |
23 | ro.key.today=azi | |
24 | sv.key.none=inget | |
25 | sv.key.today=idag | |
26 | sk.key.none=\u017eiadny | |
27 | sk.key.today=dnes | |
28 | cs.key.none=\u017e\u00e1dn\u00fd | |
29 | cs.key.today=dnes | |
30 | he.key.none=\u05E0\u05E7\u05D4 | |
31 | he.key.today=\u05D4\u05D9\u05D5\u05DD | |
32 | tr.key.none=bug\u00FCn | |
33 | tr.key.today=hi\u00E7biri | |
34 | hu.key.none=ma | |
35 | hu.key.today=semmi⏎ |
0 | package com.michaelbaranov.microba.calendar; | |
1 | ||
2 | import java.util.Calendar; | |
3 | ||
4 | import com.michaelbaranov.microba.common.Policy; | |
5 | ||
6 | /** | |
7 | * This interface is used by {@link CalendarPane} and {@link DatePicker} to | |
8 | * provide means to define hollidays and optionally provide descriptions to | |
9 | * them. | |
10 | * | |
11 | * @author Michael Baranov | |
12 | * | |
13 | */ | |
14 | public interface HolidayPolicy extends Policy { | |
15 | /** | |
16 | * This method is used to check if a date is a holliday. Holliday dates are | |
17 | * displayed by contols in a special way. | |
18 | * | |
19 | * @param source | |
20 | * a control calling this method | |
21 | * @param date | |
22 | * a date to check | |
23 | * @return <code>true</code> if given <code>date</code> is a holliday | |
24 | * <code>false</code> otherwise | |
25 | */ | |
26 | public boolean isHolliday(Object source, Calendar date); | |
27 | ||
28 | /** | |
29 | * This method is used to check if a date is a weekend date. Implementation | |
30 | * should only check day of week component of the date. | |
31 | * | |
32 | * @param source | |
33 | * a control calling this method | |
34 | * @param date | |
35 | * a date to check | |
36 | * @return <code>true</code> if given <code>date</code> is weekend date | |
37 | * <code>false</code> otherwise | |
38 | */ | |
39 | public boolean isWeekend(Object source, Calendar date); | |
40 | ||
41 | /** | |
42 | * This method is used to query a description for a holliday date. Note, | |
43 | * that implementation may return localized results based on the locale | |
44 | * passed with the date. | |
45 | * | |
46 | * @param source | |
47 | * a control calling this method | |
48 | * @param date | |
49 | * a holliday date to get the description for | |
50 | * @return a localized name (a description) for a holliday | |
51 | */ | |
52 | public String getHollidayName(Object source, Calendar date); | |
53 | ||
54 | } |
0 | package com.michaelbaranov.microba.calendar; | |
1 | ||
2 | import java.util.Calendar; | |
3 | ||
4 | import com.michaelbaranov.microba.common.Policy; | |
5 | ||
6 | /** | |
7 | * This interface is used by {@link CalendarPane} and {@link DatePicker} to | |
8 | * provide means to restrict dates in a control. | |
9 | * | |
10 | * @author Michael Baranov | |
11 | * | |
12 | */ | |
13 | public interface VetoPolicy extends Policy { | |
14 | ||
15 | /** | |
16 | * This method is used to check if a date is restricted. Restricted dates | |
17 | * can not be selected by users in a control. | |
18 | * | |
19 | * @param source | |
20 | * a control calling this method | |
21 | * @param date | |
22 | * a date to check. Is never <code>null</code> | |
23 | * @return <code>true</code> if given <code>date</code> is restricted | |
24 | * <code>false</code> otherwise | |
25 | */ | |
26 | public boolean isRestricted(Object source, Calendar date); | |
27 | ||
28 | /** | |
29 | * This method is used to check if no-date (<code>null</code> date) is | |
30 | * restricted. Restricted dates can not be selected by users in a control. | |
31 | * | |
32 | * @param source | |
33 | * a control calling this method | |
34 | * @return <code>false</code> to allow no-date, <code>true</code> | |
35 | * otherwise | |
36 | */ | |
37 | public boolean isRestrictNull(Object source); | |
38 | ||
39 | } |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | package com.michaelbaranov.microba.calendar.ui; | |
1 | ||
2 | import java.beans.PropertyVetoException; | |
3 | import java.text.ParseException; | |
4 | ||
5 | import javax.swing.plaf.ComponentUI; | |
6 | ||
7 | public abstract class CalendarPaneUI extends ComponentUI { | |
8 | ||
9 | public abstract void commit() throws PropertyVetoException, ParseException; | |
10 | ||
11 | public abstract void revert(); | |
12 | ||
13 | public abstract void observeMonth(int year, int month); | |
14 | ||
15 | } |
0 | package com.michaelbaranov.microba.calendar.ui; | |
1 | ||
2 | public abstract class DatePickerUI extends CalendarPaneUI { | |
3 | ||
4 | public abstract void showPopup(boolean visible); | |
5 | ||
6 | public abstract void setSimpeLook(boolean b); | |
7 | ||
8 | } |
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.awt.GridBagConstraints; | |
3 | import java.awt.GridBagLayout; | |
4 | import java.awt.Insets; | |
5 | import java.awt.event.ActionEvent; | |
6 | import java.awt.event.ActionListener; | |
7 | import java.beans.PropertyChangeEvent; | |
8 | import java.beans.PropertyChangeListener; | |
9 | import java.text.DateFormat; | |
10 | import java.util.Calendar; | |
11 | import java.util.Collection; | |
12 | import java.util.Date; | |
13 | import java.util.HashSet; | |
14 | import java.util.Locale; | |
15 | import java.util.Set; | |
16 | import java.util.TimeZone; | |
17 | ||
18 | import javax.swing.JButton; | |
19 | import javax.swing.JPanel; | |
20 | ||
21 | import com.michaelbaranov.microba.calendar.CalendarResources; | |
22 | import com.michaelbaranov.microba.calendar.VetoPolicy; | |
23 | import com.michaelbaranov.microba.common.PolicyEvent; | |
24 | import com.michaelbaranov.microba.common.PolicyListener; | |
25 | ||
26 | class AuxPanel extends JPanel implements PropertyChangeListener, PolicyListener { | |
27 | ||
28 | public static final String PROPERTY_NAME_LOCALE = "locale"; | |
29 | ||
30 | public static final String PROPERTY_NAME_DATE = "date"; | |
31 | ||
32 | public static final String PROPERTY_NAME_ZONE = "zone"; | |
33 | ||
34 | public static final String PROPERTY_NAME_RESOURCES = "resources"; | |
35 | ||
36 | public static final String PROPERTY_NAME_VETO_MODEL = "vetoModel"; | |
37 | ||
38 | private Locale locale; | |
39 | ||
40 | private TimeZone zone; | |
41 | ||
42 | private JButton todayButton; | |
43 | ||
44 | private JButton noneButton; | |
45 | ||
46 | private DateFormat fullDateFormat; | |
47 | ||
48 | private Date currentDate; | |
49 | ||
50 | private Set focusableComponents = new HashSet(); | |
51 | ||
52 | private VetoPolicy vetoModel; | |
53 | ||
54 | private boolean showTodayBtn; | |
55 | ||
56 | private CalendarResources resources; | |
57 | ||
58 | private boolean showNoneButton; | |
59 | ||
60 | public AuxPanel(Locale locale, TimeZone zone, VetoPolicy vetoModel, | |
61 | boolean showTodayBtn, boolean showNoneButton, | |
62 | CalendarResources resources) { | |
63 | this.locale = locale; | |
64 | this.zone = zone; | |
65 | this.vetoModel = vetoModel; | |
66 | this.showTodayBtn = showTodayBtn; | |
67 | this.showNoneButton = showNoneButton; | |
68 | this.resources = resources; | |
69 | if (vetoModel != null) | |
70 | vetoModel.addVetoPolicyListener(this); | |
71 | ||
72 | setLayout(new GridBagLayout()); | |
73 | ||
74 | todayButton = new JButton(); | |
75 | todayButton.setBorderPainted(false); | |
76 | todayButton.setContentAreaFilled(false); | |
77 | todayButton.setVisible(showTodayBtn); | |
78 | ||
79 | noneButton = new JButton(); | |
80 | noneButton.setBorderPainted(false); | |
81 | noneButton.setContentAreaFilled(false); | |
82 | noneButton.setVisible(showNoneButton); | |
83 | ||
84 | add(todayButton, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, | |
85 | GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, | |
86 | 0, 0, 0), 0, 0)); | |
87 | add(noneButton, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, | |
88 | GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, | |
89 | 0, 0, 0), 0, 0)); | |
90 | ||
91 | currentDate = new Date(); | |
92 | validateAgainstVeto(); | |
93 | createLocaleAndZoneSensitive(); | |
94 | reflectData(); | |
95 | ||
96 | todayButton.addActionListener(new ActionListener() { | |
97 | ||
98 | public void actionPerformed(ActionEvent e) { | |
99 | currentDate = new Date(); | |
100 | firePropertyChange(PROPERTY_NAME_DATE, null, currentDate); | |
101 | } | |
102 | }); | |
103 | noneButton.addActionListener(new ActionListener() { | |
104 | ||
105 | public void actionPerformed(ActionEvent e) { | |
106 | firePropertyChange(PROPERTY_NAME_DATE, null, null); | |
107 | } | |
108 | }); | |
109 | ||
110 | focusableComponents.add(todayButton); | |
111 | this.addPropertyChangeListener(this); | |
112 | ||
113 | // Timer timer = new Timer(true); | |
114 | // timer.schedule(new TimerTask() { | |
115 | // | |
116 | // public void run() { | |
117 | // currentDate = new Date(); | |
118 | // validateAgainstVeto(); | |
119 | // } | |
120 | // }, 0, 60000); | |
121 | ||
122 | } | |
123 | ||
124 | public void propertyChange(PropertyChangeEvent evt) { | |
125 | if (evt.getPropertyName().equals("focusable")) { | |
126 | Boolean value = (Boolean) evt.getNewValue(); | |
127 | todayButton.setFocusable(value.booleanValue()); | |
128 | noneButton.setFocusable(value.booleanValue()); | |
129 | } | |
130 | if (evt.getPropertyName().equals("enabled")) { | |
131 | Boolean value = (Boolean) evt.getNewValue(); | |
132 | todayButton.setEnabled(value.booleanValue()); | |
133 | noneButton.setEnabled(value.booleanValue()); | |
134 | } | |
135 | if (evt.getPropertyName().equals(PROPERTY_NAME_VETO_MODEL)) { | |
136 | VetoPolicy oldValue = (VetoPolicy) evt.getOldValue(); | |
137 | VetoPolicy newValue = (VetoPolicy) evt.getOldValue(); | |
138 | if (oldValue != null) | |
139 | oldValue.removeVetoPolicyListener(this); | |
140 | if (newValue != null) | |
141 | newValue.addVetoPolicyListener(this); | |
142 | validateAgainstVeto(); | |
143 | } | |
144 | ||
145 | } | |
146 | ||
147 | private void createLocaleAndZoneSensitive() { | |
148 | fullDateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale); | |
149 | fullDateFormat.setTimeZone(zone); | |
150 | } | |
151 | ||
152 | private void reflectData() { | |
153 | String today = resources.getResource(CalendarResources.KEY_TODAY, | |
154 | locale); | |
155 | String none = resources.getResource(CalendarResources.KEY_NONE, locale); | |
156 | todayButton.setText(today + ": " + fullDateFormat.format(currentDate)); | |
157 | noneButton.setText(none); | |
158 | ||
159 | } | |
160 | ||
161 | public Locale getLocale() { | |
162 | return locale; | |
163 | } | |
164 | ||
165 | public void setLocale(Locale locale) { | |
166 | Locale old = this.locale; | |
167 | this.locale = locale; | |
168 | firePropertyChange(PROPERTY_NAME_LOCALE, old, locale); | |
169 | createLocaleAndZoneSensitive(); | |
170 | reflectData(); | |
171 | } | |
172 | ||
173 | public Collection getFocusableComponents() { | |
174 | return focusableComponents; | |
175 | } | |
176 | ||
177 | public TimeZone getZone() { | |
178 | return zone; | |
179 | } | |
180 | ||
181 | public void setZone(TimeZone zone) { | |
182 | this.zone = zone; | |
183 | createLocaleAndZoneSensitive(); | |
184 | reflectData(); | |
185 | } | |
186 | ||
187 | public Date getDate() { | |
188 | return currentDate; | |
189 | } | |
190 | ||
191 | public VetoPolicy getVetoModel() { | |
192 | return vetoModel; | |
193 | } | |
194 | ||
195 | public void setVetoModel(VetoPolicy vetoModel) { | |
196 | VetoPolicy old = this.vetoModel; | |
197 | this.vetoModel = vetoModel; | |
198 | firePropertyChange(PROPERTY_NAME_VETO_MODEL, old, vetoModel); | |
199 | } | |
200 | ||
201 | public void policyChanged(PolicyEvent event) { | |
202 | validateAgainstVeto(); | |
203 | } | |
204 | ||
205 | private void validateAgainstVeto() { | |
206 | Calendar c = Calendar.getInstance(zone, locale); | |
207 | c.setTime(currentDate); | |
208 | if (vetoModel != null) { | |
209 | todayButton.setEnabled(!vetoModel.isRestricted(this, c)); | |
210 | noneButton.setEnabled(!vetoModel.isRestrictNull(this)); | |
211 | } else { | |
212 | todayButton.setEnabled(this.isEnabled()); | |
213 | noneButton.setEnabled(this.isEnabled()); | |
214 | } | |
215 | ||
216 | } | |
217 | ||
218 | public void setShowTodayBtn(boolean value) { | |
219 | showTodayBtn = value; | |
220 | todayButton.setVisible(showTodayBtn); | |
221 | } | |
222 | ||
223 | public void setResources(CalendarResources resources) { | |
224 | CalendarResources old = this.resources; | |
225 | this.resources = resources; | |
226 | firePropertyChange(PROPERTY_NAME_RESOURCES, old, resources); | |
227 | reflectData(); | |
228 | } | |
229 | ||
230 | public void setShowNoneButton(boolean value) { | |
231 | showNoneButton = value; | |
232 | noneButton.setVisible(showNoneButton); | |
233 | } | |
234 | ||
235 | } |
+436
-0
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.awt.GridBagConstraints; | |
3 | import java.awt.GridBagLayout; | |
4 | import java.awt.Insets; | |
5 | import java.awt.event.ActionEvent; | |
6 | import java.awt.event.FocusEvent; | |
7 | import java.awt.event.FocusListener; | |
8 | import java.awt.event.KeyEvent; | |
9 | import java.beans.PropertyChangeEvent; | |
10 | import java.beans.PropertyChangeListener; | |
11 | import java.beans.PropertyVetoException; | |
12 | import java.util.Date; | |
13 | import java.util.HashSet; | |
14 | import java.util.Locale; | |
15 | import java.util.Set; | |
16 | import java.util.TimeZone; | |
17 | ||
18 | import javax.swing.AbstractAction; | |
19 | import javax.swing.ActionMap; | |
20 | import javax.swing.InputMap; | |
21 | import javax.swing.JComponent; | |
22 | import javax.swing.KeyStroke; | |
23 | import javax.swing.SwingUtilities; | |
24 | import javax.swing.plaf.ComponentUI; | |
25 | ||
26 | import com.michaelbaranov.microba.calendar.CalendarPane; | |
27 | import com.michaelbaranov.microba.calendar.DatePicker; | |
28 | import com.michaelbaranov.microba.calendar.HolidayPolicy; | |
29 | import com.michaelbaranov.microba.calendar.VetoPolicy; | |
30 | import com.michaelbaranov.microba.calendar.ui.CalendarPaneUI; | |
31 | ||
32 | public class BasicCalendarPaneUI extends CalendarPaneUI implements | |
33 | PropertyChangeListener, FocusListener { | |
34 | ||
35 | protected static final String ESCAPE_KEY = "##CalendarPaneUI.escape##"; | |
36 | ||
37 | protected static final String ENTER_KEY = "##CalendarPaneUI.enter##"; | |
38 | ||
39 | protected CalendarPane peer; | |
40 | ||
41 | protected ClassicCalendarPanel classicPanel; | |
42 | ||
43 | protected ModernCalendarPanel modernPanel; | |
44 | ||
45 | protected AuxPanel auxPanel; | |
46 | ||
47 | protected CalendarGridPanel gridPanel; | |
48 | ||
49 | protected CalendarNumberOfWeekPanel numberOfWeekPanel; | |
50 | ||
51 | protected CalendarHeader headerPanel; | |
52 | ||
53 | protected Set focusableComponents = new HashSet(); | |
54 | ||
55 | protected ComponentListener componentListener; | |
56 | ||
57 | public static ComponentUI createUI(JComponent c) { | |
58 | return new BasicCalendarPaneUI(); | |
59 | } | |
60 | ||
61 | public void installUI(JComponent component) { | |
62 | peer = (CalendarPane) component; | |
63 | createNestedComponents(); | |
64 | addNestedComponents(); | |
65 | installListeners(); | |
66 | installKeyboardActions(); | |
67 | } | |
68 | ||
69 | public void uninstallUI(JComponent component) { | |
70 | uninstallKeyboardActions(); | |
71 | uninstallListeners(); | |
72 | removeNestedComponents(); | |
73 | destroyNestedComponents(); | |
74 | peer = null; | |
75 | } | |
76 | ||
77 | protected void uninstallKeyboardActions() { | |
78 | InputMap input = peer | |
79 | .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); | |
80 | ActionMap action = peer.getActionMap(); | |
81 | ||
82 | input.remove(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); | |
83 | input.remove(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); | |
84 | ||
85 | action.remove(ENTER_KEY); | |
86 | action.remove(ESCAPE_KEY); | |
87 | ||
88 | } | |
89 | ||
90 | protected void installKeyboardActions() { | |
91 | InputMap input = peer | |
92 | .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); | |
93 | ActionMap action = peer.getActionMap(); | |
94 | ||
95 | input.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ENTER_KEY); | |
96 | input.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), ESCAPE_KEY); | |
97 | input.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), "pgupkey"); | |
98 | input | |
99 | .put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), | |
100 | "pgdownkey"); | |
101 | ||
102 | action.put(ENTER_KEY, new AbstractAction() { | |
103 | ||
104 | public void actionPerformed(ActionEvent e) { | |
105 | peer.commitEdit(); | |
106 | } | |
107 | }); | |
108 | action.put(ESCAPE_KEY, new AbstractAction() { | |
109 | ||
110 | public void actionPerformed(ActionEvent e) { | |
111 | peer.revertEdit(); | |
112 | } | |
113 | }); | |
114 | action.put("pgupkey", new AbstractAction() { | |
115 | ||
116 | public void actionPerformed(ActionEvent e) { | |
117 | classicPanel.addMonth(1); | |
118 | } | |
119 | }); | |
120 | action.put("pgdownkey", new AbstractAction() { | |
121 | ||
122 | public void actionPerformed(ActionEvent e) { | |
123 | classicPanel.addMonth(-1); | |
124 | } | |
125 | }); | |
126 | } | |
127 | ||
128 | protected void uninstallListeners() { | |
129 | peer.removePropertyChangeListener(this); | |
130 | peer.removeFocusListener(this); | |
131 | } | |
132 | ||
133 | protected void installListeners() { | |
134 | peer.addPropertyChangeListener(this); | |
135 | peer.addFocusListener(this); | |
136 | } | |
137 | ||
138 | protected void createNestedComponents() { | |
139 | Date baseDate = peer.getDate() == null ? new Date() : peer.getDate(); | |
140 | ||
141 | classicPanel = new ClassicCalendarPanel(baseDate, peer.getLocale(), | |
142 | peer.getZone()); | |
143 | modernPanel = new ModernCalendarPanel(baseDate, peer.getLocale(), peer | |
144 | .getZone()); | |
145 | headerPanel = new CalendarHeader(peer, baseDate, peer.getLocale(), peer | |
146 | .getZone(), peer.getHolidayPolicy()); | |
147 | ||
148 | auxPanel = new AuxPanel(peer.getLocale(), peer.getZone(), peer | |
149 | .getVetoPolicy(), peer.isShowTodayButton(), peer | |
150 | .isShowNoneButton(), peer.getResources()); | |
151 | ||
152 | gridPanel = new CalendarGridPanel(peer, peer.getDate(), peer | |
153 | .getLocale(), peer.getZone(), peer.getVetoPolicy(), peer | |
154 | .getHolidayPolicy()); | |
155 | ||
156 | numberOfWeekPanel = new CalendarNumberOfWeekPanel(peer.getDate(), peer | |
157 | .getLocale(), peer.getZone()); | |
158 | ||
159 | focusableComponents.addAll(classicPanel.getFocusableComponents()); | |
160 | focusableComponents.addAll(modernPanel.getFocusableComponents()); | |
161 | focusableComponents.addAll(auxPanel.getFocusableComponents()); | |
162 | focusableComponents.addAll(gridPanel.getFocusableComponents()); | |
163 | focusableComponents.addAll(auxPanel.getFocusableComponents()); | |
164 | ||
165 | componentListener = new ComponentListener(); | |
166 | for (int i = 0; i < focusableComponents.size(); i++) | |
167 | ((JComponent) focusableComponents.toArray()[i]) | |
168 | .addFocusListener(componentListener); | |
169 | ||
170 | gridPanel.addPropertyChangeListener(componentListener); | |
171 | modernPanel.addPropertyChangeListener(componentListener); | |
172 | classicPanel.addPropertyChangeListener(componentListener); | |
173 | auxPanel.addPropertyChangeListener(componentListener); | |
174 | ||
175 | classicPanel.setEnabled(peer.isEnabled()); | |
176 | modernPanel.setEnabled(peer.isEnabled()); | |
177 | headerPanel.setEnabled(peer.isEnabled()); | |
178 | auxPanel.setEnabled(peer.isEnabled()); | |
179 | numberOfWeekPanel.setEnabled(peer.isEnabled()); | |
180 | gridPanel.setEnabled(peer.isEnabled()); | |
181 | ||
182 | } | |
183 | ||
184 | protected void destroyNestedComponents() { | |
185 | gridPanel.removePropertyChangeListener(componentListener); | |
186 | modernPanel.removePropertyChangeListener(componentListener); | |
187 | classicPanel.removePropertyChangeListener(componentListener); | |
188 | auxPanel.removePropertyChangeListener(componentListener); | |
189 | componentListener = null; | |
190 | ||
191 | for (int i = 0; i < focusableComponents.size(); i++) | |
192 | ((JComponent) focusableComponents.toArray()[i]) | |
193 | .removeFocusListener(componentListener); | |
194 | focusableComponents.clear(); | |
195 | ||
196 | classicPanel = null; | |
197 | modernPanel = null; | |
198 | headerPanel = null; | |
199 | auxPanel = null; | |
200 | gridPanel = null; | |
201 | numberOfWeekPanel = null; | |
202 | ||
203 | } | |
204 | ||
205 | protected void addNestedComponents() { | |
206 | ||
207 | peer.removeAll(); | |
208 | peer.setLayout(new GridBagLayout()); | |
209 | if ((peer.getStyle() & CalendarPane.STYLE_CLASSIC) > 0) { | |
210 | peer.add(classicPanel, new GridBagConstraints(0, 0, 2, 1, 1, 0, | |
211 | GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, | |
212 | new Insets(0, 0, 0, 0), 0, 0)); | |
213 | } else { | |
214 | peer.add(modernPanel, new GridBagConstraints(0, 0, 2, 1, 1, 0, | |
215 | GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, | |
216 | new Insets(0, 0, 0, 0), 0, 0)); | |
217 | } | |
218 | peer.add(headerPanel, new GridBagConstraints(1, 1, 1, 1, 1, 0, | |
219 | GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, | |
220 | new Insets(0, 0, 0, 0), 0, 0)); | |
221 | if (peer.isShowNumberOfWeek()) { | |
222 | peer.add(numberOfWeekPanel, new GridBagConstraints(0, 2, 1, 1, 0, | |
223 | 1, GridBagConstraints.CENTER, GridBagConstraints.VERTICAL, | |
224 | new Insets(0, 0, 0, 0), 0, 0)); | |
225 | } | |
226 | peer.add(gridPanel, new GridBagConstraints(1, 2, 1, 1, 1, 1, | |
227 | GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( | |
228 | 0, 0, 0, 0), 0, 0)); | |
229 | if (peer.isShowTodayButton()) { | |
230 | peer.add(auxPanel, new GridBagConstraints(0, 3, 2, 1, 1, 0, | |
231 | GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, | |
232 | new Insets(0, 0, 0, 0), 0, 0)); | |
233 | } | |
234 | if (peer.isShowNumberOfWeek()) { | |
235 | // TODO: | |
236 | } | |
237 | peer.revalidate(); | |
238 | peer.repaint(); | |
239 | ||
240 | } | |
241 | ||
242 | protected void removeNestedComponents() { | |
243 | peer.removeAll(); | |
244 | } | |
245 | ||
246 | protected void widgetDateChanged(Date date) { | |
247 | Date baseDate = date == null ? new Date() : date; | |
248 | ||
249 | headerPanel.setDate(baseDate); | |
250 | classicPanel.setDate(baseDate); | |
251 | modernPanel.setDate(baseDate); | |
252 | ||
253 | gridPanel.setBaseDate(baseDate); | |
254 | gridPanel.setDate(date); | |
255 | numberOfWeekPanel.setBaseDate(baseDate); | |
256 | } | |
257 | ||
258 | protected void widgetLocaleChanged(Locale newValue) { | |
259 | classicPanel.setLocale(newValue); | |
260 | modernPanel.setLocale(newValue); | |
261 | gridPanel.setLocale(newValue); | |
262 | headerPanel.setLocale(newValue); | |
263 | auxPanel.setLocale(newValue); | |
264 | numberOfWeekPanel.setLocale(newValue); | |
265 | } | |
266 | ||
267 | protected void widgetZoneChanged(TimeZone zone) { | |
268 | classicPanel.setZone(zone); | |
269 | modernPanel.setZone(zone); | |
270 | gridPanel.setZone(zone); | |
271 | headerPanel.setZone(zone); | |
272 | auxPanel.setZone(zone); | |
273 | numberOfWeekPanel.setZone(zone); | |
274 | } | |
275 | ||
276 | protected void widgetResourceChanged() { | |
277 | auxPanel.setResources(peer.getResources()); | |
278 | } | |
279 | ||
280 | public void commit() throws PropertyVetoException { | |
281 | peer.setDate(gridPanel.getDateToCommit()); | |
282 | } | |
283 | ||
284 | public void revert() { | |
285 | widgetDateChanged(peer.getDate()); | |
286 | } | |
287 | ||
288 | public void focusGained(FocusEvent e) { | |
289 | gridPanel.requestFocus(true); | |
290 | } | |
291 | ||
292 | public void focusLost(FocusEvent e) { | |
293 | } | |
294 | ||
295 | public void propertyChange(PropertyChangeEvent evt) { | |
296 | if (evt.getPropertyName().equals(CalendarPane.PROPERTY_NAME_DATE)) { | |
297 | widgetDateChanged((Date) evt.getNewValue()); | |
298 | } else if (evt.getPropertyName().equals( | |
299 | CalendarPane.PROPERTY_NAME_LOCALE)) { | |
300 | widgetLocaleChanged((Locale) evt.getNewValue()); | |
301 | } else if (evt.getPropertyName() | |
302 | .equals(CalendarPane.PROPERTY_NAME_ZONE)) { | |
303 | widgetZoneChanged((TimeZone) evt.getNewValue()); | |
304 | } else if (evt.getPropertyName().equals( | |
305 | CalendarPane.PROPERTY_NAME_VETO_POLICY)) { | |
306 | gridPanel.setVetoPolicy((VetoPolicy) evt.getNewValue()); | |
307 | auxPanel.setVetoModel((VetoPolicy) evt.getNewValue()); | |
308 | } else if (evt.getPropertyName().equals( | |
309 | CalendarPane.PROPERTY_NAME_HOLIDAY_POLICY)) { | |
310 | gridPanel.setHolidayPolicy((HolidayPolicy) evt.getNewValue()); | |
311 | headerPanel.setHolidayPolicy((HolidayPolicy) evt.getNewValue()); | |
312 | } else if (evt.getPropertyName().equals("enabled")) { | |
313 | boolean value = ((Boolean) evt.getNewValue()).booleanValue(); | |
314 | classicPanel.setEnabled(value); | |
315 | modernPanel.setEnabled(value); | |
316 | headerPanel.setEnabled(value); | |
317 | auxPanel.setEnabled(value); | |
318 | numberOfWeekPanel.setEnabled(value); | |
319 | gridPanel.setEnabled(value); | |
320 | } else if (evt.getPropertyName().equals( | |
321 | CalendarPane.PROPERTY_NAME_STYLE)) { | |
322 | addNestedComponents(); | |
323 | } else if (evt.getPropertyName().equals( | |
324 | CalendarPane.PROPERTY_NAME_SHOW_TODAY_BTN)) { | |
325 | Boolean value = (Boolean) evt.getNewValue(); | |
326 | auxPanel.setShowTodayBtn(value.booleanValue()); | |
327 | } else if (evt.getPropertyName().equals( | |
328 | DatePicker.PROPERTY_NAME_SHOW_NONE_BTN)) { | |
329 | boolean value = ((Boolean) evt.getNewValue()).booleanValue(); | |
330 | auxPanel.setShowNoneButton(value); | |
331 | } else if (evt.getPropertyName().equals( | |
332 | DatePicker.PROPERTY_NAME_SHOW_NUMBER_WEEK)) { | |
333 | addNestedComponents(); | |
334 | } else if (evt.getPropertyName().equals("focusable")) { | |
335 | Boolean value = (Boolean) evt.getNewValue(); | |
336 | classicPanel.setFocusable(value.booleanValue()); | |
337 | modernPanel.setFocusable(value.booleanValue()); | |
338 | gridPanel.setFocusable(value.booleanValue()); | |
339 | auxPanel.setFocusable(value.booleanValue()); | |
340 | } else if (evt.getPropertyName().equals( | |
341 | CalendarPane.PROPERTY_NAME_RESOURCES)) { | |
342 | widgetResourceChanged(); | |
343 | } else if (evt.getPropertyName().equals("enabled"/* | |
344 | * CalendarPane.PROPERTY_NAME_ENABLED | |
345 | */)) { | |
346 | Boolean value = (Boolean) evt.getNewValue(); | |
347 | classicPanel.setEnabled(value.booleanValue()); | |
348 | modernPanel.setEnabled(value.booleanValue()); | |
349 | gridPanel.setEnabled(value.booleanValue()); | |
350 | auxPanel.setEnabled(value.booleanValue()); | |
351 | } | |
352 | ||
353 | } | |
354 | ||
355 | protected class ComponentListener implements FocusListener, | |
356 | PropertyChangeListener { | |
357 | public void focusGained(FocusEvent e) { | |
358 | } | |
359 | ||
360 | public void focusLost(FocusEvent e) { | |
361 | boolean isFocusableComponent = focusableComponents.contains(e | |
362 | .getSource()); | |
363 | boolean isNonEmptyOpposite = e.getOppositeComponent() != null; | |
364 | if (isFocusableComponent | |
365 | && isNonEmptyOpposite | |
366 | && !SwingUtilities.isDescendingFrom(e | |
367 | .getOppositeComponent(), peer)) { | |
368 | peer.commitOrRevert(); | |
369 | } | |
370 | } | |
371 | ||
372 | public void propertyChange(PropertyChangeEvent evt) { | |
373 | if (evt.getSource() == gridPanel | |
374 | && evt.getPropertyName().equals( | |
375 | CalendarGridPanel.PROPERTY_NAME_DATE)) { | |
376 | Date newValue = (Date) evt.getNewValue(); | |
377 | try { | |
378 | peer.setDate(newValue); | |
379 | } catch (PropertyVetoException e) { | |
380 | // Ignore. Just can not happen, beacause CalendarGridPanel | |
381 | // already checked the date against current peer's | |
382 | // vetoPolicy. | |
383 | } | |
384 | } | |
385 | if (evt.getSource() == gridPanel | |
386 | && evt | |
387 | .getPropertyName() | |
388 | .equals( | |
389 | CalendarGridPanel.PROPERTY_NAME_NOTIFY_SELECTED_DATE_CLICKED)) { | |
390 | peer.fireActionEvent(); | |
391 | } | |
392 | if (evt.getSource() == gridPanel | |
393 | && evt.getPropertyName().equals( | |
394 | CalendarGridPanel.PROPERTY_NAME_BASE_DATE)) { | |
395 | Date newValue = (Date) evt.getNewValue(); | |
396 | modernPanel.setDate(newValue); | |
397 | classicPanel.setDate(newValue); | |
398 | } | |
399 | if (evt.getSource() == modernPanel | |
400 | && evt.getPropertyName().equals( | |
401 | ModernCalendarPanel.PROPERTY_NAME_DATE)) { | |
402 | Date newValue = (Date) evt.getNewValue(); | |
403 | ||
404 | gridPanel.setBaseDate(newValue); | |
405 | classicPanel.setDate(newValue); | |
406 | numberOfWeekPanel.setBaseDate(newValue); | |
407 | ||
408 | } | |
409 | if (evt.getSource() == classicPanel | |
410 | && evt.getPropertyName().equals( | |
411 | ModernCalendarPanel.PROPERTY_NAME_DATE)) { | |
412 | Date newValue = (Date) evt.getNewValue(); | |
413 | ||
414 | gridPanel.setBaseDate(newValue); | |
415 | modernPanel.setDate(newValue); | |
416 | numberOfWeekPanel.setBaseDate(newValue); | |
417 | ||
418 | } | |
419 | if (evt.getSource() == auxPanel | |
420 | && evt.getPropertyName() | |
421 | .equals(AuxPanel.PROPERTY_NAME_DATE)) { | |
422 | Date date = (Date) evt.getNewValue(); | |
423 | ||
424 | gridPanel.setDate(date); | |
425 | peer.commitEdit(); | |
426 | } | |
427 | ||
428 | } | |
429 | } | |
430 | ||
431 | public void observeMonth(int year, int month) { | |
432 | modernPanel.goToMonth(year, month); | |
433 | } | |
434 | ||
435 | } |
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.awt.BorderLayout; | |
3 | import java.awt.Insets; | |
4 | import java.awt.event.ActionEvent; | |
5 | import java.awt.event.ActionListener; | |
6 | import java.awt.event.InputEvent; | |
7 | import java.awt.event.KeyEvent; | |
8 | import java.beans.PropertyChangeEvent; | |
9 | import java.beans.PropertyChangeListener; | |
10 | import java.beans.PropertyVetoException; | |
11 | import java.text.ParseException; | |
12 | import java.util.Calendar; | |
13 | import java.util.Date; | |
14 | import java.util.TimeZone; | |
15 | ||
16 | import javax.swing.AbstractAction; | |
17 | import javax.swing.BorderFactory; | |
18 | import javax.swing.ImageIcon; | |
19 | import javax.swing.InputMap; | |
20 | import javax.swing.JButton; | |
21 | import javax.swing.JComponent; | |
22 | import javax.swing.JFormattedTextField; | |
23 | import javax.swing.JPopupMenu; | |
24 | import javax.swing.JTextField; | |
25 | import javax.swing.KeyStroke; | |
26 | import javax.swing.JFormattedTextField.AbstractFormatter; | |
27 | import javax.swing.plaf.ComponentUI; | |
28 | import javax.swing.text.DateFormatter; | |
29 | import javax.swing.text.DefaultFormatterFactory; | |
30 | ||
31 | import com.michaelbaranov.microba.calendar.CalendarPane; | |
32 | import com.michaelbaranov.microba.calendar.CalendarResources; | |
33 | import com.michaelbaranov.microba.calendar.DatePicker; | |
34 | import com.michaelbaranov.microba.calendar.HolidayPolicy; | |
35 | import com.michaelbaranov.microba.calendar.VetoPolicy; | |
36 | import com.michaelbaranov.microba.calendar.resource.Resource; | |
37 | import com.michaelbaranov.microba.calendar.ui.CalendarPaneUI; | |
38 | import com.michaelbaranov.microba.calendar.ui.DatePickerUI; | |
39 | import com.michaelbaranov.microba.common.CommitEvent; | |
40 | import com.michaelbaranov.microba.common.CommitListener; | |
41 | ||
42 | public class BasicDatePickerUI extends DatePickerUI implements | |
43 | PropertyChangeListener { | |
44 | ||
45 | protected static final String POPUP_KEY = "##BasicVetoDatePickerUI.popup##"; | |
46 | ||
47 | protected DatePicker peer; | |
48 | ||
49 | protected CalendarPane calendarPane; | |
50 | ||
51 | protected JButton button; | |
52 | ||
53 | protected JPopupMenu popup; | |
54 | ||
55 | protected JFormattedTextField field; | |
56 | ||
57 | protected ComponentListener componentListener; | |
58 | ||
59 | public static ComponentUI createUI(JComponent c) { | |
60 | return new BasicDatePickerUI(); | |
61 | } | |
62 | ||
63 | public void installUI(JComponent c) { | |
64 | peer = (DatePicker) c; | |
65 | installComponents(); | |
66 | istallListeners(); | |
67 | installKeyboardActions(); | |
68 | } | |
69 | ||
70 | public void uninstallUI(JComponent c) { | |
71 | uninstallKeyboardActions(); | |
72 | uninstallListeners(); | |
73 | uninstallComponents(); | |
74 | peer = null; | |
75 | } | |
76 | ||
77 | protected void installKeyboardActions() { | |
78 | InputMap input = peer | |
79 | .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); | |
80 | input.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_MASK), | |
81 | POPUP_KEY); | |
82 | input.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), POPUP_KEY); | |
83 | ||
84 | peer.getActionMap().put(POPUP_KEY, new AbstractAction() { | |
85 | ||
86 | public void actionPerformed(ActionEvent e) { | |
87 | showPopup(true); | |
88 | } | |
89 | }); | |
90 | ||
91 | } | |
92 | ||
93 | protected void uninstallKeyboardActions() { | |
94 | InputMap input = peer | |
95 | .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); | |
96 | input | |
97 | .remove(KeyStroke.getKeyStroke(KeyEvent.VK_C, | |
98 | InputEvent.ALT_MASK)); | |
99 | input.remove(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0)); | |
100 | ||
101 | peer.getActionMap().remove(POPUP_KEY); | |
102 | } | |
103 | ||
104 | protected void istallListeners() { | |
105 | peer.addPropertyChangeListener(this); | |
106 | } | |
107 | ||
108 | protected void uninstallListeners() { | |
109 | peer.removePropertyChangeListener(this); | |
110 | } | |
111 | ||
112 | protected void uninstallComponents() { | |
113 | button.removeActionListener(componentListener); | |
114 | field.removePropertyChangeListener(componentListener); | |
115 | ||
116 | calendarPane.removePropertyChangeListener(componentListener); | |
117 | calendarPane.removeCommitListener(componentListener); | |
118 | calendarPane.removeActionListener(componentListener); | |
119 | ||
120 | peer.remove(field); | |
121 | peer.remove(button); | |
122 | popup = null; | |
123 | calendarPane = null; | |
124 | button = null; | |
125 | field = null; | |
126 | ||
127 | } | |
128 | ||
129 | protected void installComponents() { | |
130 | field = new JFormattedTextField(createFormatterFactory()); | |
131 | field.setValue(peer.getDate()); | |
132 | field.setFocusLostBehavior(peer.getFocusLostBehavior()); | |
133 | field.setEditable(peer.isFieldEditable()); | |
134 | field.setToolTipText(peer.getToolTipText()); | |
135 | // button | |
136 | button = new JButton(); | |
137 | button.setFocusable(false); | |
138 | button.setMargin(new Insets(0, 0, 0, 0)); | |
139 | button.setToolTipText(peer.getToolTipText()); | |
140 | ||
141 | setSimpeLook(false); | |
142 | // calendar | |
143 | calendarPane = new CalendarPane(peer.getStyle()); | |
144 | calendarPane.setShowTodayButton(peer.isShowTodayButton()); | |
145 | calendarPane.setFocusLostBehavior(JFormattedTextField.REVERT); | |
146 | calendarPane.setFocusCycleRoot(true); | |
147 | calendarPane.setBorder(BorderFactory.createEmptyBorder(1, 3, 0, 3)); | |
148 | calendarPane.setStripTime(false); | |
149 | calendarPane.setLocale(peer.getLocale()); | |
150 | calendarPane.setZone(peer.getZone()); | |
151 | calendarPane.setFocusable(peer.isDropdownFocusable()); | |
152 | calendarPane.setColorOverrideMap(peer.getColorOverrideMap()); | |
153 | // popup | |
154 | popup = new JPopupMenu(); | |
155 | popup.setLayout(new BorderLayout()); | |
156 | popup.add(calendarPane, BorderLayout.CENTER); | |
157 | popup.setLightWeightPopupEnabled(true); | |
158 | // add | |
159 | peer.setLayout(new BorderLayout()); | |
160 | ||
161 | switch (peer.getPickerStyle()) { | |
162 | case DatePicker.PICKER_STYLE_FIELD_AND_BUTTON: | |
163 | peer.add(field, BorderLayout.CENTER); | |
164 | peer.add(button, BorderLayout.EAST); | |
165 | break; | |
166 | case DatePicker.PICKER_STYLE_BUTTON: | |
167 | peer.add(button, BorderLayout.EAST); | |
168 | break; | |
169 | } | |
170 | ||
171 | peer.revalidate(); | |
172 | peer.repaint(); | |
173 | ||
174 | componentListener = new ComponentListener(); | |
175 | ||
176 | button.addActionListener(componentListener); | |
177 | field.addPropertyChangeListener(componentListener); | |
178 | ||
179 | calendarPane.addPropertyChangeListener(componentListener); | |
180 | calendarPane.addCommitListener(componentListener); | |
181 | calendarPane.addActionListener(componentListener); | |
182 | ||
183 | peerDateChanged(peer.getDate()); | |
184 | } | |
185 | ||
186 | public void setSimpeLook(boolean b) { | |
187 | if (b) { | |
188 | field.setBorder(BorderFactory.createEmptyBorder()); | |
189 | button.setText("..."); | |
190 | button.setIcon(null); | |
191 | } else { | |
192 | field.setBorder(new JTextField().getBorder()); | |
193 | button.setText(""); | |
194 | button.setIcon(new ImageIcon(Resource.class | |
195 | .getResource("picker-16.png"))); | |
196 | } | |
197 | ||
198 | } | |
199 | ||
200 | public void showPopup(boolean visible) { | |
201 | if (visible) { | |
202 | ||
203 | // try to apply date to calendar pane popup, but not cause commit | |
204 | if (peer.isKeepTime()) | |
205 | try { | |
206 | AbstractFormatter formatter = field.getFormatter(); | |
207 | Date value = (Date) formatter | |
208 | .stringToValue(field.getText()); | |
209 | calendarPane | |
210 | .removePropertyChangeListener(componentListener); | |
211 | calendarPane.setDate(value); | |
212 | calendarPane.addPropertyChangeListener(componentListener); | |
213 | ||
214 | } catch (ParseException e) { | |
215 | // ignore | |
216 | } catch (PropertyVetoException e) { | |
217 | // can not happen | |
218 | } | |
219 | ||
220 | popup.show(peer, 0, peer.getHeight()); | |
221 | calendarPane.requestFocus(false); | |
222 | } else { | |
223 | popup.setVisible(false); | |
224 | } | |
225 | } | |
226 | ||
227 | public void propertyChange(PropertyChangeEvent evt) { | |
228 | if (JComponent.TOOL_TIP_TEXT_KEY.equals(evt.getPropertyName())) { | |
229 | field.setToolTipText((String) evt.getNewValue()); | |
230 | button.setToolTipText((String) evt.getNewValue()); | |
231 | } else if (evt.getPropertyName().equals(DatePicker.PROPERTY_NAME_DATE)) { | |
232 | Date newValue = (Date) evt.getNewValue(); | |
233 | peerDateChanged(newValue); | |
234 | } else if (evt.getPropertyName().equals( | |
235 | DatePicker.PROPERTY_NAME_FIELD_EDITABLE)) { | |
236 | field.setEditable(peer.isFieldEditable()); | |
237 | } else if (evt.getPropertyName().equals( | |
238 | DatePicker.PROPERTY_NAME_FOCUS_LOST_BEHAVIOR)) { | |
239 | field.setFocusLostBehavior(peer.getFocusLostBehavior()); | |
240 | } else if (evt.getPropertyName() | |
241 | .equals(DatePicker.PROPERTY_NAME_LOCALE)) { | |
242 | field.setFormatterFactory(createFormatterFactory()); | |
243 | calendarPane.setLocale(peer.getLocale()); | |
244 | } else if (evt.getPropertyName().equals( | |
245 | DatePicker.PROPERTY_NAME_DATE_FORMAT)) { | |
246 | field.setFormatterFactory(createFormatterFactory()); | |
247 | } else if (evt.getPropertyName().equals(DatePicker.PROPERTY_NAME_ZONE)) { | |
248 | field.setFormatterFactory(createFormatterFactory()); | |
249 | calendarPane.setZone((TimeZone) evt.getNewValue()); | |
250 | } else if (evt.getPropertyName().equals( | |
251 | DatePicker.PROPERTY_NAME_SHOW_TODAY_BTN)) { | |
252 | boolean value = ((Boolean) evt.getNewValue()).booleanValue(); | |
253 | calendarPane.setShowTodayButton(value); | |
254 | } else if (evt.getPropertyName().equals( | |
255 | DatePicker.PROPERTY_NAME_SHOW_NONE_BTN)) { | |
256 | boolean value = ((Boolean) evt.getNewValue()).booleanValue(); | |
257 | calendarPane.setShowNoneButton(value); | |
258 | } else if (evt.getPropertyName().equals( | |
259 | DatePicker.PROPERTY_NAME_SHOW_NUMBER_WEEK)) { | |
260 | boolean value = ((Boolean) evt.getNewValue()).booleanValue(); | |
261 | calendarPane.setShowNumberOfWeek(value); | |
262 | } else if (evt.getPropertyName().equals(DatePicker.PROPERTY_NAME_STYLE)) { | |
263 | int value = ((Integer) evt.getNewValue()).intValue(); | |
264 | calendarPane.setStyle(value); | |
265 | } else if (evt.getPropertyName().equals( | |
266 | DatePicker.PROPERTY_NAME_VETO_POLICY)) { | |
267 | calendarPane.setVetoPolicy((VetoPolicy) evt.getNewValue()); | |
268 | } else if (evt.getPropertyName().equals( | |
269 | DatePicker.PROPERTY_NAME_HOLIDAY_POLICY)) { | |
270 | calendarPane.setHolidayPolicy((HolidayPolicy) evt.getNewValue()); | |
271 | } else if (evt.getPropertyName().equals("focusable")) { | |
272 | boolean value = ((Boolean) evt.getNewValue()).booleanValue(); | |
273 | field.setFocusable(value); | |
274 | } else if (evt.getPropertyName().equals( | |
275 | DatePicker.PROPERTY_NAME_RESOURCES)) { | |
276 | CalendarResources resources = (CalendarResources) evt.getNewValue(); | |
277 | calendarPane.setResources(resources); | |
278 | } else if (evt.getPropertyName().equals("enabled"/* | |
279 | * DatePicker.PROPERTY_NAME_ENABLED | |
280 | */)) { | |
281 | boolean value = ((Boolean) evt.getNewValue()).booleanValue(); | |
282 | field.setEnabled(value); | |
283 | button.setEnabled(value); | |
284 | } else if (evt.getPropertyName().equals( | |
285 | DatePicker.PROPERTY_NAME_PICKER_STYLE)) { | |
286 | peer.updateUI(); | |
287 | } else if (evt.getPropertyName().equals( | |
288 | DatePicker.PROPERTY_NAME_DROPDOWN_FOCUSABLE)) { | |
289 | calendarPane.setFocusable(peer.isDropdownFocusable()); | |
290 | } | |
291 | } | |
292 | ||
293 | private void peerDateChanged(Date newValue) { | |
294 | try { | |
295 | calendarPane.removePropertyChangeListener(componentListener); | |
296 | calendarPane.setDate(newValue); | |
297 | calendarPane.addPropertyChangeListener(componentListener); | |
298 | } catch (PropertyVetoException e) { | |
299 | // Ignore. CalendarPane has no VetoModel here. | |
300 | } | |
301 | field.removePropertyChangeListener(componentListener); | |
302 | field.setValue(newValue); | |
303 | field.addPropertyChangeListener(componentListener); | |
304 | } | |
305 | ||
306 | private DefaultFormatterFactory createFormatterFactory() { | |
307 | return new DefaultFormatterFactory(new DateFormatter(peer | |
308 | .getDateFormat())); | |
309 | } | |
310 | ||
311 | protected class ComponentListener implements ActionListener, | |
312 | PropertyChangeListener, CommitListener { | |
313 | public void actionPerformed(ActionEvent e) { | |
314 | ||
315 | if (e.getSource() != calendarPane) | |
316 | showPopup(true); | |
317 | else | |
318 | showPopup(false); | |
319 | ||
320 | } | |
321 | ||
322 | public void propertyChange(PropertyChangeEvent evt) { | |
323 | if (evt.getSource() == calendarPane) { | |
324 | if (CalendarPane.PROPERTY_NAME_DATE.equals(evt | |
325 | .getPropertyName())) { | |
326 | showPopup(false); | |
327 | ||
328 | Date fieldValue = null; | |
329 | try { | |
330 | AbstractFormatter formatter = field.getFormatter(); | |
331 | fieldValue = (Date) formatter.stringToValue(field | |
332 | .getText()); | |
333 | ||
334 | } catch (ParseException e) { | |
335 | fieldValue = (Date) field.getValue(); | |
336 | } | |
337 | ||
338 | if (fieldValue != null || evt.getNewValue() != null) { | |
339 | ||
340 | if (peer.isKeepTime() && fieldValue != null | |
341 | && evt.getNewValue() != null) { | |
342 | ||
343 | Calendar fieldCal = Calendar.getInstance(peer | |
344 | .getZone(), peer.getLocale()); | |
345 | fieldCal.setTime(fieldValue); | |
346 | ||
347 | Calendar valueCal = Calendar.getInstance(peer | |
348 | .getZone(), peer.getLocale()); | |
349 | valueCal.setTime((Date) evt.getNewValue()); | |
350 | ||
351 | // era | |
352 | fieldCal.set(Calendar.ERA, valueCal | |
353 | .get(Calendar.ERA)); | |
354 | // year | |
355 | fieldCal.set(Calendar.YEAR, valueCal | |
356 | .get(Calendar.YEAR)); | |
357 | // month | |
358 | fieldCal.set(Calendar.MONTH, valueCal | |
359 | .get(Calendar.MONTH)); | |
360 | // date | |
361 | fieldCal.set(Calendar.DAY_OF_MONTH, valueCal | |
362 | .get(Calendar.DAY_OF_MONTH)); | |
363 | field.setValue(fieldCal.getTime()); | |
364 | } else | |
365 | field.setValue((Date) evt.getNewValue()); | |
366 | ||
367 | } | |
368 | } | |
369 | } | |
370 | if (evt.getSource() == field) { | |
371 | if ("value".equals(evt.getPropertyName())) { | |
372 | Date value = (Date) field.getValue(); | |
373 | ||
374 | try { | |
375 | peer.setDate(value); | |
376 | } catch (PropertyVetoException e) { | |
377 | field.setValue(peer.getDate()); | |
378 | } | |
379 | } | |
380 | } | |
381 | ||
382 | } | |
383 | ||
384 | public void commit(CommitEvent action) { | |
385 | showPopup(false); | |
386 | if (field.getValue() != null || calendarPane.getDate() != null) | |
387 | field.setValue(calendarPane.getDate()); | |
388 | } | |
389 | ||
390 | public void revert(CommitEvent action) { | |
391 | showPopup(false); | |
392 | ||
393 | } | |
394 | } | |
395 | ||
396 | public void commit() throws PropertyVetoException, ParseException { | |
397 | field.commitEdit(); | |
398 | } | |
399 | ||
400 | public void revert() { | |
401 | peerDateChanged(peer.getDate()); | |
402 | } | |
403 | ||
404 | public void observeMonth(int year, int month) { | |
405 | CalendarPaneUI ui = (CalendarPaneUI) calendarPane.getUI(); | |
406 | ui.observeMonth(year, month); | |
407 | } | |
408 | ||
409 | } |
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.awt.Graphics; | |
4 | import java.awt.GridLayout; | |
5 | import java.awt.event.ActionEvent; | |
6 | import java.awt.event.FocusEvent; | |
7 | import java.awt.event.FocusListener; | |
8 | import java.awt.event.KeyEvent; | |
9 | import java.awt.event.KeyListener; | |
10 | import java.awt.event.MouseEvent; | |
11 | import java.awt.event.MouseListener; | |
12 | import java.beans.PropertyChangeEvent; | |
13 | import java.beans.PropertyChangeListener; | |
14 | import java.util.Calendar; | |
15 | import java.util.Collection; | |
16 | import java.util.Date; | |
17 | import java.util.HashSet; | |
18 | import java.util.Locale; | |
19 | import java.util.Set; | |
20 | import java.util.TimeZone; | |
21 | ||
22 | import javax.swing.AbstractAction; | |
23 | import javax.swing.BorderFactory; | |
24 | import javax.swing.InputMap; | |
25 | import javax.swing.JComponent; | |
26 | import javax.swing.JLabel; | |
27 | import javax.swing.JPanel; | |
28 | import javax.swing.KeyStroke; | |
29 | import javax.swing.SwingConstants; | |
30 | import javax.swing.UIManager; | |
31 | ||
32 | import com.michaelbaranov.microba.Microba; | |
33 | import com.michaelbaranov.microba.calendar.CalendarColors; | |
34 | import com.michaelbaranov.microba.calendar.CalendarPane; | |
35 | import com.michaelbaranov.microba.calendar.HolidayPolicy; | |
36 | import com.michaelbaranov.microba.calendar.VetoPolicy; | |
37 | import com.michaelbaranov.microba.common.PolicyEvent; | |
38 | import com.michaelbaranov.microba.common.PolicyListener; | |
39 | ||
40 | class CalendarGridPanel extends JPanel implements FocusListener, | |
41 | PolicyListener, PropertyChangeListener, MouseListener, KeyListener { | |
42 | ||
43 | public static final String PROPERTY_NAME_DATE = "date"; | |
44 | ||
45 | public static final String PROPERTY_NAME_BASE_DATE = "baseDate"; | |
46 | ||
47 | public static final String PROPERTY_NAME_LOCALE = "locale"; | |
48 | ||
49 | public static final String PROPERTY_NAME_ZONE = "zone"; | |
50 | ||
51 | public static final String PROPERTY_NAME_VETO_POLICY = "vetoPolicy"; | |
52 | ||
53 | private static final String PROPERTY_NAME_HOLIDAY_POLICY = "holidayPolicy"; | |
54 | ||
55 | // Not a rela property, ad-hoc approach to notify about clicking selected | |
56 | // date lable | |
57 | public static final String PROPERTY_NAME_NOTIFY_SELECTED_DATE_CLICKED = "##same date clicked##"; | |
58 | ||
59 | private CalendarPane peer; | |
60 | ||
61 | private Date date; | |
62 | ||
63 | private Date baseDate; | |
64 | ||
65 | private Date focusDate; | |
66 | ||
67 | private Locale locale; | |
68 | ||
69 | private TimeZone zone; | |
70 | ||
71 | private VetoPolicy vetoPolicy; | |
72 | ||
73 | private DateLabel labels[] = new DateLabel[42]; | |
74 | ||
75 | private Set focusableComponents = new HashSet(); | |
76 | ||
77 | private boolean explicitDateSetToNullFlag; | |
78 | ||
79 | private HolidayPolicy holidayPolicy; | |
80 | ||
81 | private Color focusColor; | |
82 | ||
83 | private Color restrictedColor; | |
84 | ||
85 | private Color gridBgEn; | |
86 | ||
87 | private Color gridBgDis; | |
88 | ||
89 | private Color gridFgEn; | |
90 | ||
91 | private Color gridFgDis; | |
92 | ||
93 | private Color selBgEn; | |
94 | ||
95 | private Color selBgDis; | |
96 | ||
97 | private Color wkFgEn; | |
98 | ||
99 | private Color wkFgDis; | |
100 | ||
101 | private Color holFgEn; | |
102 | ||
103 | private Color holFgDis; | |
104 | ||
105 | public CalendarGridPanel(CalendarPane peer, Date date, Locale locale, | |
106 | TimeZone zone, VetoPolicy vetoDateModel, HolidayPolicy holidayPolicy) { | |
107 | ||
108 | this.peer = peer; | |
109 | ||
110 | focusColor = Microba.getOverridenColor( | |
111 | CalendarColors.COLOR_CALENDAR_GRID_FOCUS, peer, UIManager | |
112 | .getColor("TabbedPane.focus")); | |
113 | restrictedColor = Microba.getOverridenColor( | |
114 | CalendarColors.COLOR_CALENDAR_GRID_RESTRICTED, peer, Color.RED); | |
115 | gridBgEn = Microba.getOverridenColor( | |
116 | CalendarColors.COLOR_CALENDAR_GRID_BACKGROUND_ENABLED, peer, | |
117 | UIManager.getColor("TextField.background")); | |
118 | ||
119 | gridBgDis = Microba.getOverridenColor( | |
120 | CalendarColors.COLOR_CALENDAR_GRID_BACKGROUND_DISABLED, peer, | |
121 | UIManager.getColor("TextField.background")); | |
122 | ||
123 | gridFgEn = Microba.getOverridenColor( | |
124 | CalendarColors.COLOR_CALENDAR_GRID_FOREGROUND_ENABLED, peer, | |
125 | UIManager.getColor("TextField.foreground")); | |
126 | ||
127 | gridFgDis = Microba.getOverridenColor( | |
128 | CalendarColors.COLOR_CALENDAR_GRID_FOREGROUND_DISABLED, peer, | |
129 | UIManager.getColor("controlText")); | |
130 | ||
131 | selBgEn = Microba | |
132 | .getOverridenColor( | |
133 | CalendarColors.COLOR_CALENDAR_GRID_SELECTION_BACKGROUND_ENABLED, | |
134 | peer, UIManager | |
135 | .getColor("ComboBox.selectionBackground")); | |
136 | selBgDis = Microba | |
137 | .getOverridenColor( | |
138 | CalendarColors.COLOR_CALENDAR_GRID_SELECTION_BACKGROUND_DISABLED, | |
139 | peer, UIManager | |
140 | .getColor("ComboBox.selectionBackground")); | |
141 | ||
142 | wkFgDis = Microba.getOverridenColor( | |
143 | CalendarColors.COLOR_CALENDAR_GRID_WEEKEND_FOREGROUND_DISABLED, | |
144 | peer, gridFgDis); | |
145 | wkFgEn = Microba.getOverridenColor( | |
146 | CalendarColors.COLOR_CALENDAR_GRID_WEEKEND_FOREGROUND_ENABLED, | |
147 | peer, Color.RED); | |
148 | holFgDis = Microba.getOverridenColor( | |
149 | CalendarColors.COLOR_CALENDAR_GRID_HOLIDAY_FOREGROUND_DISABLED, | |
150 | peer, gridFgDis); | |
151 | holFgEn = Microba.getOverridenColor( | |
152 | CalendarColors.COLOR_CALENDAR_GRID_HOLIDAY_FOREGROUND_ENABLED, | |
153 | peer, Color.RED); | |
154 | ||
155 | this.locale = locale; | |
156 | this.zone = zone; | |
157 | this.date = date; | |
158 | this.baseDate = date == null ? new Date() : date; | |
159 | this.explicitDateSetToNullFlag = date == null ? true : false; | |
160 | this.focusDate = getFocusDateForDate(date); | |
161 | this.vetoPolicy = vetoDateModel; | |
162 | this.holidayPolicy = holidayPolicy; | |
163 | if (this.vetoPolicy != null) | |
164 | this.vetoPolicy.addVetoPolicyListener(this); | |
165 | if (this.holidayPolicy != null) | |
166 | this.holidayPolicy.addVetoPolicyListener(this); | |
167 | this.addPropertyChangeListener(this); | |
168 | ||
169 | setLayout(new GridLayout(6, 7, 2, 2)); | |
170 | ||
171 | for (int i = 0; i < 42; i++) { | |
172 | DateLabel l = new DateLabel(i); | |
173 | labels[i] = l; | |
174 | l.setText(String.valueOf(i)); | |
175 | l.addMouseListener(this); | |
176 | add(l); | |
177 | } | |
178 | focusableComponents.add(this); | |
179 | addKeyListener(this); | |
180 | setFocusable(true); | |
181 | ||
182 | // TODO: move the following to key listeners? | |
183 | InputMap input = this.getInputMap(JComponent.WHEN_FOCUSED); | |
184 | input.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), | |
185 | "##microba.commit##"); | |
186 | input.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), | |
187 | "##microba.commit##"); | |
188 | ||
189 | this.getActionMap().put("##microba.commit##", new AbstractAction() { | |
190 | ||
191 | public void actionPerformed(ActionEvent e) { | |
192 | Calendar c = getCalendar(focusDate); | |
193 | if (vetoPolicy == null || !vetoPolicy.isRestricted(this, c)) { | |
194 | setDate(focusDate); | |
195 | } | |
196 | ||
197 | } | |
198 | }); | |
199 | addFocusListener(this); | |
200 | // addMouseListener(this); | |
201 | setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); | |
202 | reflectData(); | |
203 | } | |
204 | ||
205 | public void focusGained(FocusEvent e) { | |
206 | setBorder(BorderFactory.createLineBorder(focusColor)); | |
207 | reflectFocusedDate(); | |
208 | } | |
209 | ||
210 | public void focusLost(FocusEvent e) { | |
211 | setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); | |
212 | reflectFocusedDate(); | |
213 | ||
214 | } | |
215 | ||
216 | private void setSelectedByIndex(int i) { | |
217 | DateLabel label = labels[i]; | |
218 | if (label.isVisible()) { | |
219 | int day = Integer.parseInt(label.getText()); | |
220 | Calendar c = getCalendar(baseDate); | |
221 | c.set(Calendar.DAY_OF_MONTH, day); | |
222 | setDate(c.getTime()); | |
223 | } | |
224 | } | |
225 | ||
226 | private Calendar getCalendar(Date date) { | |
227 | Calendar c = Calendar.getInstance(zone, locale); | |
228 | c.setTime(date); | |
229 | return c; | |
230 | } | |
231 | ||
232 | private int getSelectedIndex() { | |
233 | if (date == null) | |
234 | return -1; | |
235 | Calendar bc = getCalendar(baseDate); | |
236 | Calendar sc = getCalendar(date); | |
237 | // selectd date visible in base month | |
238 | if (bc.get(Calendar.ERA) == sc.get(Calendar.ERA) | |
239 | && bc.get(Calendar.YEAR) == sc.get(Calendar.YEAR) | |
240 | && bc.get(Calendar.MONTH) == sc.get(Calendar.MONTH)) { | |
241 | bc.set(Calendar.DAY_OF_MONTH, 1); | |
242 | int skipBefore = bc.get(Calendar.DAY_OF_WEEK) | |
243 | - bc.getFirstDayOfWeek(); | |
244 | if (skipBefore < 0) | |
245 | skipBefore = 7 + skipBefore; | |
246 | int selDay = sc.get(Calendar.DAY_OF_MONTH); | |
247 | return skipBefore + selDay - 1; | |
248 | } else | |
249 | return -1; | |
250 | } | |
251 | ||
252 | private void setFocusedByIndex(int i) { | |
253 | DateLabel label = labels[i]; | |
254 | if (label.isVisible()) { | |
255 | int day = Integer.parseInt(label.getText()); | |
256 | Calendar c = getCalendar(baseDate); | |
257 | c.set(Calendar.DAY_OF_MONTH, day); | |
258 | setFocusDate(c.getTime()); | |
259 | } | |
260 | } | |
261 | ||
262 | private int getFocusedIndex() { | |
263 | Calendar bc = getCalendar(baseDate); | |
264 | Calendar fc = getCalendar(focusDate); | |
265 | bc.set(Calendar.DAY_OF_MONTH, 1); | |
266 | int skipBefore = bc.get(Calendar.DAY_OF_WEEK) - bc.getFirstDayOfWeek(); | |
267 | if (skipBefore < 0) | |
268 | skipBefore = 7 + skipBefore; | |
269 | int selDay = fc.get(Calendar.DAY_OF_MONTH); | |
270 | int maxDay = bc.getActualMaximum(Calendar.DAY_OF_MONTH); | |
271 | if (selDay > maxDay) | |
272 | selDay = maxDay; | |
273 | return skipBefore + selDay - 1; | |
274 | } | |
275 | ||
276 | private void reflectData() { | |
277 | setBackground(isEnabled() ? gridBgEn : gridBgDis); | |
278 | ||
279 | reflectBaseDate(); | |
280 | reflectSelectedDate(); | |
281 | reflectFocusedDate(); | |
282 | } | |
283 | ||
284 | private void reflectFocusedDate() { | |
285 | int focusedIndex = getFocusedIndex(); | |
286 | DateLabel l = labels[focusedIndex]; | |
287 | l.setFocused(isFocusOwner()); | |
288 | } | |
289 | ||
290 | private void reflectSelectedDate() { | |
291 | int selIndex = getSelectedIndex(); | |
292 | if (selIndex > -1) { | |
293 | DateLabel l = labels[selIndex]; | |
294 | l.setSelected(true); | |
295 | } | |
296 | } | |
297 | ||
298 | private void reflectBaseDate() { | |
299 | Calendar calendar = getCalendar(baseDate); | |
300 | calendar.set(Calendar.DAY_OF_MONTH, 1); | |
301 | ||
302 | int skipBefore = calendar.get(Calendar.DAY_OF_WEEK) | |
303 | - calendar.getFirstDayOfWeek(); | |
304 | if (skipBefore < 0) | |
305 | skipBefore = 7 + skipBefore; | |
306 | ||
307 | int activeDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); | |
308 | ||
309 | int day = 1; | |
310 | calendar.setTime(baseDate); | |
311 | calendar.set(Calendar.DAY_OF_MONTH, 1); | |
312 | ||
313 | for (int i = 0; i < 42; i++) { | |
314 | DateLabel l = labels[i]; | |
315 | l.setBackground(isEnabled() ? selBgEn : selBgDis); | |
316 | l.setSelected(false); | |
317 | l.setFocused(false); | |
318 | l.setEnabled(isEnabled()); | |
319 | ||
320 | if (i < skipBefore) { | |
321 | l.setText(""); | |
322 | l.setVisible(false); | |
323 | } | |
324 | if (i >= skipBefore && i < skipBefore + activeDays) { | |
325 | l.setVisible(true); | |
326 | l.setText(String.valueOf(day)); | |
327 | if (vetoPolicy != null) | |
328 | l.setBanned(vetoPolicy.isRestricted(this.peer, calendar)); | |
329 | else | |
330 | l.setBanned(false); | |
331 | ||
332 | if (holidayPolicy != null) { | |
333 | l.setDate(calendar.getTime()); | |
334 | l | |
335 | .setHolliday(holidayPolicy.isHolliday(this.peer, | |
336 | calendar)); | |
337 | l.setWeekend(holidayPolicy.isWeekend(this.peer, calendar)); | |
338 | } else { | |
339 | l.setHolliday(false); | |
340 | l.setWeekend(false); | |
341 | } | |
342 | day++; | |
343 | calendar.add(Calendar.DAY_OF_MONTH, 1); | |
344 | } | |
345 | if (i >= skipBefore + activeDays) { | |
346 | l.setText(""); | |
347 | l.setVisible(false); | |
348 | } | |
349 | ||
350 | } | |
351 | } | |
352 | ||
353 | public Date getDate() { | |
354 | return date; | |
355 | } | |
356 | ||
357 | public void setDate(Date date) { | |
358 | Date old = this.date; | |
359 | this.date = date; | |
360 | this.explicitDateSetToNullFlag = date == null ? true : false; | |
361 | this.focusDate = getFocusDateForDate(date); | |
362 | if (old != null || date != null) | |
363 | firePropertyChange(PROPERTY_NAME_DATE, old, date); | |
364 | reflectData(); | |
365 | } | |
366 | ||
367 | private Date getFocusDateForDate(Date date) { | |
368 | if (date == null) { | |
369 | Calendar c = getCalendar(baseDate); | |
370 | c.set(Calendar.DAY_OF_MONTH, 1); | |
371 | return c.getTime(); | |
372 | } | |
373 | return date; | |
374 | } | |
375 | ||
376 | public Locale getLocale() { | |
377 | return locale; | |
378 | } | |
379 | ||
380 | public void setLocale(Locale locale) { | |
381 | Locale old = this.locale; | |
382 | this.locale = locale; | |
383 | firePropertyChange(PROPERTY_NAME_LOCALE, old, locale); | |
384 | reflectData(); | |
385 | } | |
386 | ||
387 | public VetoPolicy getVetoPolicy() { | |
388 | return vetoPolicy; | |
389 | } | |
390 | ||
391 | public void setVetoPolicy(VetoPolicy vetoModel) { | |
392 | VetoPolicy old = this.getVetoPolicy(); | |
393 | this.vetoPolicy = vetoModel; | |
394 | firePropertyChange(PROPERTY_NAME_VETO_POLICY, old, vetoModel); | |
395 | reflectData(); | |
396 | } | |
397 | ||
398 | public HolidayPolicy getHolidayPolicy() { | |
399 | return holidayPolicy; | |
400 | } | |
401 | ||
402 | public void setHolidayPolicy(HolidayPolicy holidayPolicy) { | |
403 | HolidayPolicy old = this.getHolidayPolicy(); | |
404 | this.holidayPolicy = holidayPolicy; | |
405 | firePropertyChange(PROPERTY_NAME_HOLIDAY_POLICY, old, holidayPolicy); | |
406 | reflectData(); | |
407 | } | |
408 | ||
409 | public TimeZone getZone() { | |
410 | return zone; | |
411 | } | |
412 | ||
413 | public void setZone(TimeZone zone) { | |
414 | TimeZone old = this.zone; | |
415 | this.zone = zone; | |
416 | firePropertyChange(PROPERTY_NAME_ZONE, old, zone); | |
417 | reflectData(); | |
418 | } | |
419 | ||
420 | public Collection getFocusableComponents() { | |
421 | return focusableComponents; | |
422 | } | |
423 | ||
424 | class DateLabel extends JLabel { | |
425 | ||
426 | private Date date; | |
427 | ||
428 | private int id; | |
429 | ||
430 | private boolean focused; | |
431 | ||
432 | private boolean selected; | |
433 | ||
434 | private boolean weekend; | |
435 | ||
436 | private boolean banned; | |
437 | ||
438 | private boolean holliday; | |
439 | ||
440 | public DateLabel(int id) { | |
441 | super(); | |
442 | this.id = id; | |
443 | setHorizontalAlignment(SwingConstants.CENTER); | |
444 | ||
445 | setFocused(false); | |
446 | setSelected(false); | |
447 | setWeekend(false); | |
448 | setBanned(false); | |
449 | setHolliday(false); | |
450 | } | |
451 | ||
452 | public void setHolliday(boolean b) { | |
453 | holliday = b; | |
454 | update(); | |
455 | repaint(); | |
456 | } | |
457 | ||
458 | public int getId() { | |
459 | return id; | |
460 | } | |
461 | ||
462 | public boolean isFocused() { | |
463 | return focused; | |
464 | } | |
465 | ||
466 | public void setFocused(boolean focused) { | |
467 | this.focused = focused; | |
468 | ||
469 | update(); | |
470 | repaint(); | |
471 | } | |
472 | ||
473 | public boolean isSelected() { | |
474 | return selected; | |
475 | } | |
476 | ||
477 | public void setSelected(boolean selected) { | |
478 | this.selected = selected; | |
479 | update(); | |
480 | repaint(); | |
481 | } | |
482 | ||
483 | private void update() { | |
484 | // background is determined by selected state | |
485 | // foreground by the rest | |
486 | updateBg(); | |
487 | updateFg(); | |
488 | udapteBorder(); | |
489 | // Tooltip: | |
490 | updateTooltip(); | |
491 | } | |
492 | ||
493 | private void updateTooltip() { | |
494 | if (holidayPolicy != null && holliday) { | |
495 | Calendar c = Calendar.getInstance(zone, locale); | |
496 | c.setTime(date); | |
497 | setToolTipText(holidayPolicy.getHollidayName(this, c)); | |
498 | } else | |
499 | setToolTipText(null); | |
500 | } | |
501 | ||
502 | private void udapteBorder() { | |
503 | if (isFocused() && isEnabled()) { | |
504 | setBorder(BorderFactory | |
505 | .createLineBorder(banned ? restrictedColor : focusColor)); | |
506 | ||
507 | } else | |
508 | setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); | |
509 | } | |
510 | ||
511 | private void updateFg() { | |
512 | if (isHolliday()) { | |
513 | setForeground(isEnabled() ? holFgEn : holFgDis); | |
514 | } else { | |
515 | if (isWeekend()) { | |
516 | setForeground(isEnabled() ? wkFgEn : wkFgDis); | |
517 | } else { | |
518 | setForeground(isEnabled() ? gridFgEn : gridFgDis); | |
519 | } | |
520 | } | |
521 | ||
522 | } | |
523 | ||
524 | private void updateBg() { | |
525 | if (isSelected()) { | |
526 | setOpaque(true); | |
527 | setBackground(isEnabled() ? selBgEn : selBgDis); | |
528 | } else | |
529 | setOpaque(false); | |
530 | } | |
531 | ||
532 | public boolean isWeekend() { | |
533 | return weekend; | |
534 | } | |
535 | ||
536 | public void setWeekend(boolean weekend) { | |
537 | this.weekend = weekend; | |
538 | } | |
539 | ||
540 | public boolean isBanned() { | |
541 | return banned; | |
542 | } | |
543 | ||
544 | public void setBanned(boolean banned) { | |
545 | this.banned = banned; | |
546 | update(); | |
547 | repaint(); | |
548 | } | |
549 | ||
550 | public void paint(Graphics g) { | |
551 | ||
552 | if (isBanned()) { | |
553 | g.setColor(restrictedColor); | |
554 | ||
555 | // variant 1: full size cross | |
556 | g.drawLine(2, 2, getWidth() - 4, getHeight() - 4); | |
557 | g.drawLine(2, getHeight() - 4, getWidth() - 4, 2); | |
558 | ||
559 | // variant 2: smaller cross (left upper corner) | |
560 | // g.drawLine(1, 1, 8, 8); | |
561 | // g.drawLine(1, 8, 8, 1); | |
562 | ||
563 | } | |
564 | super.paint(g); | |
565 | } | |
566 | ||
567 | public boolean isHolliday() { | |
568 | return holliday; | |
569 | } | |
570 | ||
571 | public Date getDate() { | |
572 | return date; | |
573 | } | |
574 | ||
575 | public void setDate(Date date) { | |
576 | this.date = date; | |
577 | } | |
578 | ||
579 | } | |
580 | ||
581 | public void policyChanged(PolicyEvent event) { | |
582 | reflectData(); | |
583 | } | |
584 | ||
585 | public void propertyChange(PropertyChangeEvent evt) { | |
586 | if (evt.getPropertyName().equals(PROPERTY_NAME_VETO_POLICY)) { | |
587 | VetoPolicy oldValue = (VetoPolicy) evt.getOldValue(); | |
588 | VetoPolicy newValue = (VetoPolicy) evt.getOldValue(); | |
589 | if (oldValue != null) | |
590 | oldValue.removeVetoPolicyListener(this); | |
591 | if (newValue != null) | |
592 | newValue.addVetoPolicyListener(this); | |
593 | reflectData(); | |
594 | } | |
595 | } | |
596 | ||
597 | public Date getBaseDate() { | |
598 | return baseDate; | |
599 | } | |
600 | ||
601 | public void setBaseDate(Date baseDate) { | |
602 | // TODO: throw away the following 2 lines? | |
603 | // if (baseDate == null) | |
604 | // baseDate = new Date(); | |
605 | Date old = this.baseDate; | |
606 | this.baseDate = baseDate; | |
607 | firePropertyChange(PROPERTY_NAME_BASE_DATE, old, baseDate); | |
608 | ||
609 | // update focus date | |
610 | Calendar bc = getCalendar(baseDate); | |
611 | Calendar fc = getCalendar(focusDate); | |
612 | int focDate = fc.get(Calendar.DAY_OF_MONTH); | |
613 | int maxDay = bc.getActualMaximum(Calendar.DAY_OF_MONTH); | |
614 | if (focDate > maxDay) | |
615 | focDate = maxDay; | |
616 | bc.set(Calendar.DAY_OF_MONTH, focDate); | |
617 | focusDate = bc.getTime(); | |
618 | ||
619 | reflectData(); | |
620 | } | |
621 | ||
622 | private Date getFocusDate() { | |
623 | return focusDate; | |
624 | } | |
625 | ||
626 | private void setFocusDate(Date focusDate) { | |
627 | this.focusDate = focusDate; | |
628 | explicitDateSetToNullFlag = false; | |
629 | reflectData(); | |
630 | } | |
631 | ||
632 | public void mouseClicked(MouseEvent e) { | |
633 | if (!isEnabled()) | |
634 | return; | |
635 | requestFocusInWindow(); | |
636 | DateLabel l = (DateLabel) e.getSource(); | |
637 | if (l.isVisible()) { | |
638 | int id = Integer.parseInt(l.getText()); | |
639 | Calendar c = getCalendar(baseDate); | |
640 | c.set(Calendar.DAY_OF_MONTH, id); | |
641 | if (vetoPolicy == null || !vetoPolicy.isRestricted(this, c)) { | |
642 | boolean selected = l.isSelected(); | |
643 | setDate(c.getTime()); | |
644 | if (selected) | |
645 | firePropertyChange( | |
646 | PROPERTY_NAME_NOTIFY_SELECTED_DATE_CLICKED, null, | |
647 | new Integer(id)); | |
648 | } | |
649 | } | |
650 | } | |
651 | ||
652 | public void mousePressed(MouseEvent e) { | |
653 | ||
654 | } | |
655 | ||
656 | public void mouseReleased(MouseEvent e) { | |
657 | } | |
658 | ||
659 | public void mouseEntered(MouseEvent e) { | |
660 | } | |
661 | ||
662 | public void mouseExited(MouseEvent e) { | |
663 | } | |
664 | ||
665 | public void keyTyped(KeyEvent e) { | |
666 | ||
667 | } | |
668 | ||
669 | public void keyPressed(KeyEvent e) { | |
670 | if (!isEnabled()) | |
671 | return; | |
672 | int id = getFocusedIndex(); | |
673 | int row = id / 7; | |
674 | int col = id % 7; | |
675 | if (e.getKeyCode() == KeyEvent.VK_DOWN) { | |
676 | row++; | |
677 | if (row < 6) { | |
678 | setFocusedByIndex(row * 7 + col); | |
679 | } | |
680 | } | |
681 | if (e.getKeyCode() == KeyEvent.VK_UP) { | |
682 | row--; | |
683 | if (row >= 0) { | |
684 | setFocusedByIndex(row * 7 + col); | |
685 | } | |
686 | } | |
687 | if (e.getKeyCode() == KeyEvent.VK_LEFT) { | |
688 | col--; | |
689 | if (col >= 0) { | |
690 | setFocusedByIndex(row * 7 + col); | |
691 | } | |
692 | } | |
693 | if (e.getKeyCode() == KeyEvent.VK_RIGHT) { | |
694 | col++; | |
695 | if (col < 7) { | |
696 | setFocusedByIndex(row * 7 + col); | |
697 | } | |
698 | } | |
699 | } | |
700 | ||
701 | public void keyReleased(KeyEvent e) { | |
702 | } | |
703 | ||
704 | public Date getDateToCommit() { | |
705 | Calendar c = getCalendar(focusDate); | |
706 | if (explicitDateSetToNullFlag | |
707 | || (vetoPolicy != null && vetoPolicy.isRestricted(this, c))) | |
708 | return date; | |
709 | return focusDate; | |
710 | } | |
711 | ||
712 | public void setEnabled(boolean enabled) { | |
713 | super.setEnabled(enabled); | |
714 | reflectData(); | |
715 | } | |
716 | ||
717 | } |
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.awt.Font; | |
4 | import java.awt.GridLayout; | |
5 | import java.text.SimpleDateFormat; | |
6 | import java.util.Calendar; | |
7 | import java.util.Date; | |
8 | import java.util.Locale; | |
9 | import java.util.TimeZone; | |
10 | ||
11 | import javax.swing.BorderFactory; | |
12 | import javax.swing.JLabel; | |
13 | import javax.swing.JPanel; | |
14 | import javax.swing.SwingConstants; | |
15 | import javax.swing.UIManager; | |
16 | ||
17 | import com.michaelbaranov.microba.Microba; | |
18 | import com.michaelbaranov.microba.calendar.CalendarPane; | |
19 | import com.michaelbaranov.microba.calendar.HolidayPolicy; | |
20 | ||
21 | class CalendarHeader extends JPanel { | |
22 | ||
23 | private Locale locale; | |
24 | ||
25 | private TimeZone zone; | |
26 | ||
27 | private Date date; | |
28 | ||
29 | private HolidayPolicy holidayPolicy; | |
30 | ||
31 | private Color backgroundColorActive; | |
32 | ||
33 | private Color backgroundColorInactive; | |
34 | ||
35 | private Color foregroundColorActive; | |
36 | ||
37 | private Color foregroundColorInactive; | |
38 | ||
39 | private Color foregroundColorWeekendEnabled; | |
40 | ||
41 | private Color foregroundColorWeekendDisabled; | |
42 | ||
43 | public CalendarHeader(CalendarPane peer, Date date, Locale locale, | |
44 | TimeZone zone, HolidayPolicy holidayPolicy) { | |
45 | super(); | |
46 | ||
47 | backgroundColorActive = Microba.getOverridenColor( | |
48 | CalendarPane.COLOR_CALENDAR_HEADER_BACKGROUND_ENABLED, peer, | |
49 | UIManager.getColor("activeCaption")); | |
50 | backgroundColorInactive = Microba.getOverridenColor( | |
51 | CalendarPane.COLOR_CALENDAR_HEADER_BACKGROUND_DISABLED, peer, | |
52 | UIManager.getColor("inactiveCaption")); | |
53 | foregroundColorActive = Microba.getOverridenColor( | |
54 | CalendarPane.COLOR_CALENDAR_HEADER_FOREGROUND_ENABLED, peer, | |
55 | UIManager.getColor("controlText")); | |
56 | foregroundColorInactive = Microba.getOverridenColor( | |
57 | CalendarPane.COLOR_CALENDAR_HEADER_FOREGROUND_DISABLED, peer, | |
58 | UIManager.getColor("textInactiveText")); | |
59 | foregroundColorWeekendEnabled = Microba.getOverridenColor( | |
60 | CalendarPane.COLOR_CALENDAR_HEADER_FOREGROUND_WEEKEND_ENABLED, | |
61 | peer, Color.RED); | |
62 | foregroundColorWeekendDisabled = Microba.getOverridenColor( | |
63 | CalendarPane.COLOR_CALENDAR_HEADER_FOREGROUND_WEEKEND_DISABLED, | |
64 | peer, foregroundColorInactive); | |
65 | ||
66 | this.locale = locale; | |
67 | this.zone = zone; | |
68 | this.date = date; | |
69 | this.holidayPolicy = holidayPolicy; | |
70 | reflectData(); | |
71 | } | |
72 | ||
73 | private void reflectData() { | |
74 | ||
75 | Calendar cal = Calendar.getInstance(zone, locale); | |
76 | cal.setTime(date == null ? new Date() : date); | |
77 | ||
78 | SimpleDateFormat fmt = new SimpleDateFormat("E", locale); | |
79 | fmt.setTimeZone(zone); | |
80 | ||
81 | int numDaysInWeek = cal.getActualMaximum(Calendar.DAY_OF_WEEK) | |
82 | - cal.getActualMinimum(Calendar.DAY_OF_WEEK) + 1; | |
83 | int firstDayOfWeek = cal.getFirstDayOfWeek(); | |
84 | ||
85 | cal.set(Calendar.DAY_OF_WEEK, firstDayOfWeek); | |
86 | ||
87 | removeAll(); | |
88 | setLayout(new GridLayout(1, numDaysInWeek, 2, 2)); | |
89 | ||
90 | setBackground(isEnabled() ? backgroundColorActive | |
91 | : backgroundColorInactive); | |
92 | ||
93 | for (int i = 0; i < numDaysInWeek; i++) { | |
94 | JLabel label = new JLabel(); | |
95 | // TODO: add option to control limit length: | |
96 | label.setText(fmt.format(cal.getTime())/* .substring(0,1) */); | |
97 | label.setForeground(isEnabled() ? foregroundColorActive | |
98 | : foregroundColorInactive); | |
99 | label.setHorizontalAlignment(SwingConstants.CENTER); | |
100 | label.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0)); | |
101 | Font boldFont = label.getFont().deriveFont(Font.BOLD); | |
102 | label.setFont(boldFont); | |
103 | add(label); | |
104 | ||
105 | boolean isHolliday = false; | |
106 | if (holidayPolicy != null) { | |
107 | isHolliday = holidayPolicy.isWeekend(this, cal); | |
108 | } | |
109 | ||
110 | if (isHolliday) | |
111 | label.setForeground(isEnabled() ? foregroundColorWeekendEnabled | |
112 | : foregroundColorWeekendDisabled); | |
113 | ||
114 | cal.add(Calendar.DAY_OF_WEEK, 1); | |
115 | } | |
116 | setBorder(BorderFactory.createEmptyBorder(0, 1, 0, 1)); | |
117 | revalidate(); | |
118 | repaint(); | |
119 | ||
120 | } | |
121 | ||
122 | public void setLocale(Locale locale) { | |
123 | this.locale = locale; | |
124 | reflectData(); | |
125 | } | |
126 | ||
127 | public void setDate(Date date) { | |
128 | this.date = date; | |
129 | reflectData(); | |
130 | } | |
131 | ||
132 | public TimeZone getZone() { | |
133 | return zone; | |
134 | } | |
135 | ||
136 | public void setZone(TimeZone zone) { | |
137 | this.zone = zone; | |
138 | reflectData(); | |
139 | } | |
140 | ||
141 | public void setHolidayPolicy(HolidayPolicy holidayPolicy) { | |
142 | this.holidayPolicy = holidayPolicy; | |
143 | reflectData(); | |
144 | ||
145 | } | |
146 | ||
147 | public void setEnabled(boolean enabled) { | |
148 | super.setEnabled(enabled); | |
149 | reflectData(); | |
150 | } | |
151 | ||
152 | } |
+130
-0
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.awt.Dimension; | |
4 | import java.awt.FontMetrics; | |
5 | import java.awt.Graphics; | |
6 | import java.awt.GridLayout; | |
7 | import java.util.Calendar; | |
8 | import java.util.Date; | |
9 | import java.util.Locale; | |
10 | import java.util.TimeZone; | |
11 | ||
12 | import javax.swing.BorderFactory; | |
13 | import javax.swing.JLabel; | |
14 | import javax.swing.JPanel; | |
15 | import javax.swing.UIManager; | |
16 | ||
17 | public class CalendarNumberOfWeekPanel extends JPanel /* | |
18 | * implements | |
19 | * PropertyChangeListener | |
20 | */{ | |
21 | ||
22 | public static final String PROPERTY_NAME_BASE_DATE = "baseDate"; | |
23 | ||
24 | public static final String PROPERTY_NAME_LOCALE = "locale"; | |
25 | ||
26 | public static final String PROPERTY_NAME_ZONE = "zone"; | |
27 | ||
28 | private Color backgroundColorActive = UIManager.getColor("activeCaption"); | |
29 | ||
30 | private Color backgroundColorInactive = UIManager | |
31 | .getColor("inactiveCaption"); | |
32 | ||
33 | private Date baseDate; | |
34 | ||
35 | private Locale locale; | |
36 | ||
37 | private TimeZone zone; | |
38 | ||
39 | private JLabel[] labels = new JLabel[6]; | |
40 | ||
41 | public CalendarNumberOfWeekPanel(Date baseDate, Locale locale, | |
42 | TimeZone timeZone) { | |
43 | super(); | |
44 | this.baseDate = baseDate == null ? new Date() : baseDate; | |
45 | this.locale = locale; | |
46 | this.zone = timeZone; | |
47 | ||
48 | setLayout(new GridLayout(6, 1, 2, 2)); | |
49 | ||
50 | for (int i = 0; i < 6; i++) { | |
51 | JLabel l = new JLabel(); | |
52 | labels[i] = l; | |
53 | ||
54 | add(l); | |
55 | } | |
56 | ||
57 | setBorder(BorderFactory.createEmptyBorder(1, 4, 1, 4)); | |
58 | ||
59 | reflectBaseDate(); | |
60 | } | |
61 | ||
62 | public void setBaseDate(Date baseDate) { | |
63 | this.baseDate = baseDate; | |
64 | reflectBaseDate(); | |
65 | } | |
66 | ||
67 | private void reflectBaseDate() { | |
68 | Calendar calendar = getCalendar(baseDate); | |
69 | calendar.set(Calendar.DAY_OF_MONTH, 1); | |
70 | ||
71 | int skipBefore = calendar.get(Calendar.DAY_OF_WEEK) | |
72 | - calendar.getFirstDayOfWeek(); | |
73 | if (skipBefore < 0) | |
74 | skipBefore = 7 + skipBefore; | |
75 | ||
76 | int activeDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); | |
77 | ||
78 | int numActiveWeeks = (activeDays + skipBefore) / 7; | |
79 | if ((activeDays + skipBefore) % 7 > 0) | |
80 | numActiveWeeks++; | |
81 | ||
82 | int startWeek = calendar.get(Calendar.WEEK_OF_YEAR); | |
83 | ||
84 | for (int i = 0; i < 6; i++) { | |
85 | labels[i].setText(startWeek > 0 && numActiveWeeks > 0 ? String | |
86 | .valueOf(startWeek) : ""); | |
87 | ||
88 | labels[i].setForeground(isEnabled() ? UIManager | |
89 | .getColor("controlText") : UIManager | |
90 | .getColor("textInactiveText")); | |
91 | ||
92 | startWeek++; | |
93 | numActiveWeeks--; | |
94 | } | |
95 | ||
96 | setBackground(isEnabled() ? backgroundColorActive | |
97 | : backgroundColorInactive); | |
98 | } | |
99 | ||
100 | private Calendar getCalendar(Date date) { | |
101 | Calendar c = Calendar.getInstance(zone, locale); | |
102 | c.setTime(date); | |
103 | return c; | |
104 | } | |
105 | ||
106 | public void setLocale(Locale locale) { | |
107 | this.locale = locale; | |
108 | reflectBaseDate(); | |
109 | } | |
110 | ||
111 | public void setZone(TimeZone zone) { | |
112 | this.zone = zone; | |
113 | reflectBaseDate(); | |
114 | } | |
115 | ||
116 | public void paint(Graphics g) { | |
117 | FontMetrics fm = g.getFontMetrics(labels[0].getFont()); | |
118 | Dimension dimension = new Dimension(fm.stringWidth("00") + 8, 1); | |
119 | setMinimumSize(dimension); | |
120 | setPreferredSize(dimension); | |
121 | super.paint(g); | |
122 | } | |
123 | ||
124 | public void setEnabled(boolean enabled) { | |
125 | super.setEnabled(enabled); | |
126 | reflectBaseDate(); | |
127 | } | |
128 | ||
129 | } |
+265
-0
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.awt.Dimension; | |
3 | import java.awt.Font; | |
4 | import java.awt.GridBagConstraints; | |
5 | import java.awt.GridBagLayout; | |
6 | import java.awt.Insets; | |
7 | import java.awt.event.ActionEvent; | |
8 | import java.awt.event.ActionListener; | |
9 | import java.beans.PropertyChangeEvent; | |
10 | import java.beans.PropertyChangeListener; | |
11 | import java.text.DateFormat; | |
12 | import java.text.SimpleDateFormat; | |
13 | import java.util.Calendar; | |
14 | import java.util.Collection; | |
15 | import java.util.Date; | |
16 | import java.util.HashSet; | |
17 | import java.util.Locale; | |
18 | import java.util.Set; | |
19 | import java.util.TimeZone; | |
20 | ||
21 | import javax.swing.ImageIcon; | |
22 | import javax.swing.JButton; | |
23 | import javax.swing.JLabel; | |
24 | import javax.swing.JPanel; | |
25 | import javax.swing.SwingConstants; | |
26 | ||
27 | import com.michaelbaranov.microba.calendar.resource.Resource; | |
28 | ||
29 | class ClassicCalendarPanel extends JPanel implements | |
30 | PropertyChangeListener { | |
31 | ||
32 | public static final String PROPERTY_NAME_DATE = "date"; | |
33 | ||
34 | public static final String PROPERTY_NAME_LOCALE = "locale"; | |
35 | ||
36 | public static final String PROPERTY_NAME_ZONE = "zone"; | |
37 | ||
38 | private Locale locale; | |
39 | ||
40 | private TimeZone zone; | |
41 | ||
42 | private Calendar calendar; | |
43 | ||
44 | private JButton prevButton; | |
45 | ||
46 | private JButton nextButton; | |
47 | ||
48 | private JLabel selectedDateLabel; | |
49 | ||
50 | private DateFormat format; | |
51 | ||
52 | private Set focusableComponents = new HashSet(); | |
53 | ||
54 | private JButton fastPrevButton; | |
55 | ||
56 | private JButton fastNextButton; | |
57 | ||
58 | public ClassicCalendarPanel(Date aDate, Locale aLocale, TimeZone zone) { | |
59 | this.locale = aLocale; | |
60 | this.zone = zone; | |
61 | ||
62 | prevButton = new JButton(); | |
63 | nextButton = new JButton(); | |
64 | ||
65 | fastPrevButton = new JButton(); | |
66 | fastNextButton = new JButton(); | |
67 | ||
68 | nextButton.setIcon(new ImageIcon(Resource.class.getResource("forward-16.png"))); | |
69 | prevButton.setIcon(new ImageIcon(Resource.class.getResource("back-16.png"))); | |
70 | fastNextButton.setIcon(new ImageIcon(Resource.class.getResource("forward-fast-16.png"))); | |
71 | fastPrevButton.setIcon(new ImageIcon(Resource.class.getResource("back-fast-16.png"))); | |
72 | prevButton.setMargin(new Insets(0, 0, 0, 0)); | |
73 | nextButton.setMargin(new Insets(0, 0, 0, 0)); | |
74 | fastPrevButton.setMargin(new Insets(0, 0, 0, 0)); | |
75 | fastNextButton.setMargin(new Insets(0, 0, 0, 0)); | |
76 | ||
77 | Dimension psz = nextButton.getPreferredSize(); | |
78 | Dimension npsz = new Dimension(psz.height, psz.height); | |
79 | ||
80 | nextButton.setPreferredSize(npsz); | |
81 | prevButton.setPreferredSize(npsz); | |
82 | ||
83 | selectedDateLabel = new JLabel(); | |
84 | selectedDateLabel.setHorizontalAlignment(SwingConstants.CENTER); | |
85 | selectedDateLabel.setFont(selectedDateLabel.getFont().deriveFont( | |
86 | Font.BOLD)); | |
87 | setLayout(new GridBagLayout()); | |
88 | add(fastPrevButton, new GridBagConstraints(0, 0, 1, 1, 0, 0, | |
89 | GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( | |
90 | 0, 0, 3, 0), 0, 0)); | |
91 | add(prevButton, new GridBagConstraints(1, 0, 1, 1, 0, 0, | |
92 | GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( | |
93 | 0, 0, 3, 0), 0, 0)); | |
94 | add(selectedDateLabel, new GridBagConstraints(2, 0, 1, 1, 1.0, 0, | |
95 | GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, | |
96 | new Insets(0, 3, 3, 3), 0, 0)); | |
97 | add(nextButton, new GridBagConstraints(3, 0, 1, 1, 0, 0, | |
98 | GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( | |
99 | 0, 0, 3, 0), 0, 0)); | |
100 | add(fastNextButton, new GridBagConstraints(4, 0, 1, 1, 0, 0, | |
101 | GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( | |
102 | 0, 0, 3, 0), 0, 0)); | |
103 | ||
104 | nextButton.addActionListener(new ActionListener() { | |
105 | ||
106 | public void actionPerformed(ActionEvent e) { | |
107 | Date old = calendar.getTime(); | |
108 | calendar.add(Calendar.MONTH, 1); | |
109 | firePropertyChange(PROPERTY_NAME_DATE, old, getDate()); | |
110 | reflectData(); | |
111 | } | |
112 | }); | |
113 | prevButton.addActionListener(new ActionListener() { | |
114 | ||
115 | public void actionPerformed(ActionEvent e) { | |
116 | Date old = calendar.getTime(); | |
117 | calendar.add(Calendar.MONTH, -1); | |
118 | firePropertyChange(PROPERTY_NAME_DATE, old, getDate()); | |
119 | reflectData(); | |
120 | } | |
121 | }); | |
122 | fastNextButton.addActionListener(new ActionListener() { | |
123 | ||
124 | public void actionPerformed(ActionEvent e) { | |
125 | Date old = calendar.getTime(); | |
126 | calendar.add(Calendar.YEAR, 1); | |
127 | firePropertyChange(PROPERTY_NAME_DATE, old, getDate()); | |
128 | reflectData(); | |
129 | } | |
130 | }); | |
131 | fastPrevButton.addActionListener(new ActionListener() { | |
132 | ||
133 | public void actionPerformed(ActionEvent e) { | |
134 | Date old = calendar.getTime(); | |
135 | calendar.add(Calendar.YEAR, -1); | |
136 | firePropertyChange(PROPERTY_NAME_DATE, old, getDate()); | |
137 | reflectData(); | |
138 | } | |
139 | }); | |
140 | ||
141 | this.addPropertyChangeListener(this); | |
142 | ||
143 | focusableComponents.add(prevButton); | |
144 | focusableComponents.add(nextButton); | |
145 | focusableComponents.add(fastNextButton); | |
146 | focusableComponents.add(fastPrevButton); | |
147 | ||
148 | createLocaleAndZoneSensitive(); | |
149 | calendar.setTime(aDate); | |
150 | reflectData(); | |
151 | } | |
152 | ||
153 | private void createLocaleAndZoneSensitive() { | |
154 | if (calendar != null) { | |
155 | Date old = calendar.getTime(); | |
156 | calendar = Calendar.getInstance(zone, locale); | |
157 | calendar.setTime(old); | |
158 | } else | |
159 | calendar = Calendar.getInstance(zone, locale); | |
160 | ||
161 | format = new SimpleDateFormat("MMMMM yyyy", locale); | |
162 | format.setTimeZone(zone); | |
163 | ||
164 | setPreferredLabelSize(); | |
165 | ||
166 | } | |
167 | ||
168 | private void setPreferredLabelSize() { | |
169 | Calendar c = Calendar.getInstance(zone, locale); | |
170 | c.setTime(getDate()); | |
171 | ||
172 | JLabel l = new JLabel(); | |
173 | l.setFont(selectedDateLabel.getFont()); | |
174 | ||
175 | int maxWidth = Integer.MIN_VALUE; | |
176 | for (int i = 0; i <= c.getActualMaximum(Calendar.MONTH); i++) { | |
177 | c.set(Calendar.MONTH, i); | |
178 | String text = format.format(c.getTime()); | |
179 | l.setText(text); | |
180 | int w = l.getPreferredSize().width; | |
181 | if (w > maxWidth) | |
182 | maxWidth = w; | |
183 | } | |
184 | Dimension d = l.getPreferredSize(); | |
185 | d.width = maxWidth + 10; | |
186 | selectedDateLabel.setMinimumSize(d); | |
187 | selectedDateLabel.setPreferredSize(d); | |
188 | this.revalidate(); | |
189 | } | |
190 | ||
191 | private void reflectData() { | |
192 | selectedDateLabel.setText(format.format(calendar.getTime())); | |
193 | } | |
194 | ||
195 | public Date getDate() { | |
196 | return calendar.getTime(); | |
197 | } | |
198 | ||
199 | public void setDate(Date date) { | |
200 | Date old = getDate(); | |
201 | calendar.setTime(date); | |
202 | firePropertyChange(PROPERTY_NAME_DATE, old, date); | |
203 | reflectData(); | |
204 | } | |
205 | ||
206 | public Locale getLocale() { | |
207 | return locale; | |
208 | } | |
209 | ||
210 | public void setLocale(Locale locale) { | |
211 | Locale old = this.locale; | |
212 | this.locale = locale; | |
213 | createLocaleAndZoneSensitive(); | |
214 | firePropertyChange(PROPERTY_NAME_LOCALE, old, locale); | |
215 | reflectData(); | |
216 | } | |
217 | ||
218 | public TimeZone getZone() { | |
219 | return zone; | |
220 | } | |
221 | ||
222 | public void setZone(TimeZone zone) { | |
223 | TimeZone old = this.zone; | |
224 | this.zone = zone; | |
225 | createLocaleAndZoneSensitive(); | |
226 | firePropertyChange(PROPERTY_NAME_ZONE, old, locale); | |
227 | reflectData(); | |
228 | } | |
229 | ||
230 | public void propertyChange(PropertyChangeEvent evt) { | |
231 | if (evt.getPropertyName().equals("focusable")) { | |
232 | Boolean value = (Boolean) evt.getNewValue(); | |
233 | prevButton.setFocusable(value.booleanValue()); | |
234 | nextButton.setFocusable(value.booleanValue()); | |
235 | fastNextButton.setFocusable(value.booleanValue()); | |
236 | fastPrevButton.setFocusable(value.booleanValue()); | |
237 | } | |
238 | if (evt.getPropertyName().equals("enabled")) { | |
239 | Boolean value = (Boolean) evt.getNewValue(); | |
240 | prevButton.setEnabled(value.booleanValue()); | |
241 | nextButton.setEnabled(value.booleanValue()); | |
242 | fastNextButton.setEnabled(value.booleanValue()); | |
243 | fastPrevButton.setEnabled(value.booleanValue()); | |
244 | } | |
245 | ||
246 | } | |
247 | ||
248 | public Collection getFocusableComponents() { | |
249 | return focusableComponents; | |
250 | } | |
251 | ||
252 | public void addMonth(int m) { | |
253 | int modM = m > 0 ? m : -m; | |
254 | int sign = m > 0 ? 1 : -1; | |
255 | Date old = calendar.getTime(); | |
256 | for (int i = 0; i < modM; i++) { | |
257 | calendar.add(Calendar.MONTH, sign); | |
258 | } | |
259 | firePropertyChange(PROPERTY_NAME_DATE, old, getDate()); | |
260 | reflectData(); | |
261 | ||
262 | } | |
263 | ||
264 | } |
+177
-0
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.awt.Component; | |
3 | import java.awt.GridBagConstraints; | |
4 | import java.awt.GridBagLayout; | |
5 | import java.awt.Insets; | |
6 | import java.beans.PropertyChangeEvent; | |
7 | import java.beans.PropertyChangeListener; | |
8 | import java.util.Calendar; | |
9 | import java.util.Collection; | |
10 | import java.util.Date; | |
11 | import java.util.HashSet; | |
12 | import java.util.Locale; | |
13 | import java.util.Set; | |
14 | import java.util.TimeZone; | |
15 | ||
16 | import javax.swing.JComboBox; | |
17 | import javax.swing.JPanel; | |
18 | ||
19 | class ModernCalendarPanel extends JPanel implements PropertyChangeListener { | |
20 | ||
21 | public static final String PROPERTY_NAME_DATE = "date"; | |
22 | ||
23 | public static final String PROPERTY_NAME_LOCALE = "locale"; | |
24 | ||
25 | public static final String PROPERTY_NAME_ZONE = "zone"; | |
26 | ||
27 | private Date date; | |
28 | ||
29 | private Locale locale; | |
30 | ||
31 | private TimeZone zone; | |
32 | ||
33 | private YearSpinnerModel yearSpinnerModel; | |
34 | ||
35 | private NoGroupingSpinner yearSpinner; | |
36 | ||
37 | private MonthComboBoxModel monthComboBoxModel; | |
38 | ||
39 | private MonthComboBoxRenderer monthComboBoxRenderer; | |
40 | ||
41 | private JComboBox monthCombo; | |
42 | ||
43 | private Set focusableComponents = new HashSet(); | |
44 | ||
45 | public ModernCalendarPanel(Date aDate, Locale aLocale, TimeZone zone) { | |
46 | this.date = aDate; | |
47 | this.locale = aLocale; | |
48 | this.zone = zone; | |
49 | ||
50 | monthComboBoxModel = new MonthComboBoxModel(aDate, aLocale, zone); | |
51 | monthComboBoxRenderer = new MonthComboBoxRenderer(aLocale, zone); | |
52 | monthCombo = new JComboBox(monthComboBoxModel); | |
53 | ||
54 | monthCombo.setRenderer(monthComboBoxRenderer); | |
55 | ||
56 | yearSpinnerModel = new YearSpinnerModel(aDate, aLocale, zone); | |
57 | yearSpinner = new NoGroupingSpinner(yearSpinnerModel); | |
58 | ||
59 | setLayout(new GridBagLayout()); | |
60 | add(monthCombo, new GridBagConstraints(0, 0, 1, 1, 1.0, 0, | |
61 | GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, | |
62 | 0, 3, 0), 0, 0)); | |
63 | add(yearSpinner, new GridBagConstraints(1, 0, 1, 1, 0, 0, | |
64 | GridBagConstraints.EAST, GridBagConstraints.VERTICAL, | |
65 | new Insets(0, 3, 3, 0), 0, 0)); | |
66 | ||
67 | focusableComponents.add(yearSpinner); | |
68 | focusableComponents.add(monthCombo); | |
69 | ||
70 | monthComboBoxModel | |
71 | .addPropertyChangeListener(new PropertyChangeListener() { | |
72 | ||
73 | public void propertyChange(PropertyChangeEvent evt) { | |
74 | if (evt.getPropertyName().equals( | |
75 | MonthComboBoxModel.PROPERTY_NAME_DATE)) { | |
76 | Date newDate = (Date) evt.getNewValue(); | |
77 | setDate(newDate); | |
78 | } | |
79 | } | |
80 | }); | |
81 | yearSpinnerModel | |
82 | .addPropertyChangeListener(new PropertyChangeListener() { | |
83 | ||
84 | public void propertyChange(PropertyChangeEvent evt) { | |
85 | if (evt.getPropertyName().equals( | |
86 | YearSpinnerModel.PROPERTY_NAME_DATE)) { | |
87 | Date newDate = (Date) evt.getNewValue(); | |
88 | setDate(newDate); | |
89 | } | |
90 | } | |
91 | }); | |
92 | this.addPropertyChangeListener(this); | |
93 | ||
94 | } | |
95 | ||
96 | public void setEnabled(boolean enabled) { | |
97 | super.setEnabled(enabled); | |
98 | monthCombo.setEnabled(enabled); | |
99 | yearSpinner.setEnabled(enabled); | |
100 | } | |
101 | ||
102 | public void setFocusable(boolean focusable) { | |
103 | super.setFocusable(focusable); | |
104 | monthCombo.setFocusable(focusable); | |
105 | yearSpinner.setFocusable(focusable); | |
106 | } | |
107 | ||
108 | public Date getDate() { | |
109 | return date; | |
110 | } | |
111 | ||
112 | public void setDate(Date date) { | |
113 | Date oldDate = this.date; | |
114 | this.date = date; | |
115 | firePropertyChange(PROPERTY_NAME_DATE, oldDate, date); | |
116 | monthComboBoxModel.setDate(date); | |
117 | yearSpinnerModel.setDate(date); | |
118 | } | |
119 | ||
120 | public Locale getLocale() { | |
121 | return locale; | |
122 | } | |
123 | ||
124 | public void setLocale(Locale locale) { | |
125 | Locale old = this.locale; | |
126 | this.locale = locale; | |
127 | monthComboBoxRenderer.setLocale(locale); | |
128 | monthComboBoxModel.setLocale(locale); | |
129 | yearSpinnerModel.setLocale(locale); | |
130 | firePropertyChange(PROPERTY_NAME_LOCALE, old, locale); | |
131 | } | |
132 | ||
133 | public Collection getFocusableComponents() { | |
134 | return focusableComponents; | |
135 | } | |
136 | ||
137 | public TimeZone getZone() { | |
138 | return zone; | |
139 | } | |
140 | ||
141 | public void setZone(TimeZone zone) { | |
142 | TimeZone old = this.zone; | |
143 | this.zone = zone; | |
144 | monthComboBoxRenderer.setZone(zone); | |
145 | monthComboBoxModel.setZone(zone); | |
146 | yearSpinnerModel.setZone(zone); | |
147 | firePropertyChange(PROPERTY_NAME_ZONE, old, zone); | |
148 | } | |
149 | ||
150 | public void propertyChange(PropertyChangeEvent evt) { | |
151 | if (evt.getPropertyName().equals("focusable")) { | |
152 | boolean value = ((Boolean) evt.getNewValue()).booleanValue(); | |
153 | yearSpinner.setFocusable(value); | |
154 | Component children[] = yearSpinner.getEditor().getComponents(); | |
155 | for (int i = 0; i < children.length; i++) | |
156 | children[i].setFocusable(value); | |
157 | monthCombo.setFocusable(value); | |
158 | } | |
159 | if (evt.getPropertyName().equals("enabled")) { | |
160 | boolean value = ((Boolean) evt.getNewValue()).booleanValue(); | |
161 | yearSpinner.setEnabled(value); | |
162 | monthCombo.setEnabled(value); | |
163 | } | |
164 | } | |
165 | ||
166 | public void goToMonth(int year, int month) { | |
167 | Date date = getDate(); | |
168 | Calendar calendar = Calendar.getInstance(zone,locale); | |
169 | calendar.setTime(date); | |
170 | calendar.set(Calendar.YEAR, year); | |
171 | calendar.set(Calendar.MONTH, month); | |
172 | setDate(calendar.getTime()); | |
173 | ||
174 | } | |
175 | ||
176 | } |
+135
-0
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.beans.PropertyChangeListener; | |
3 | import java.beans.PropertyChangeSupport; | |
4 | import java.util.Calendar; | |
5 | import java.util.Date; | |
6 | import java.util.Locale; | |
7 | import java.util.TimeZone; | |
8 | ||
9 | import javax.swing.AbstractListModel; | |
10 | import javax.swing.ComboBoxModel; | |
11 | ||
12 | class MonthComboBoxModel extends AbstractListModel implements | |
13 | ComboBoxModel { | |
14 | ||
15 | public static final String PROPERTY_NAME_LOCALE = "locale"; | |
16 | ||
17 | public static final String PROPERTY_NAME_DATE = "date"; | |
18 | ||
19 | private PropertyChangeSupport changeSupport = new PropertyChangeSupport( | |
20 | this); | |
21 | ||
22 | private Calendar calendar; | |
23 | ||
24 | private Locale locale; | |
25 | ||
26 | private TimeZone zone; | |
27 | ||
28 | public MonthComboBoxModel(Date date, Locale locale, TimeZone zone) { | |
29 | super(); | |
30 | this.locale = locale; | |
31 | this.zone = zone; | |
32 | ||
33 | createLocaleAndZoneSensitive(); | |
34 | calendar.setTime(date); | |
35 | } | |
36 | ||
37 | private void createLocaleAndZoneSensitive() { | |
38 | if (calendar != null) { | |
39 | Date old = calendar.getTime(); | |
40 | calendar = Calendar.getInstance(zone, locale); | |
41 | calendar.setTime(old); | |
42 | } else | |
43 | calendar = Calendar.getInstance(zone, locale); | |
44 | } | |
45 | ||
46 | public Locale getLocale() { | |
47 | return locale; | |
48 | } | |
49 | ||
50 | public void setLocale(Locale locale) { | |
51 | Locale old = this.locale; | |
52 | this.locale = locale; | |
53 | createLocaleAndZoneSensitive(); | |
54 | changeSupport.firePropertyChange(PROPERTY_NAME_LOCALE, old, locale); | |
55 | fireContentsChanged(this, 0, getSize() - 1); | |
56 | } | |
57 | ||
58 | public Date getDate() { | |
59 | return calendar.getTime(); | |
60 | } | |
61 | ||
62 | public void setDate(Date date) { | |
63 | Date old = getDate(); | |
64 | calendar.setTime(date); | |
65 | changeSupport.firePropertyChange(PROPERTY_NAME_DATE, old, date); | |
66 | fireContentsChanged(this, 0, getSize() - 1); | |
67 | } | |
68 | ||
69 | public void setSelectedItem(Object anItem) { | |
70 | Date aDate = (Date) anItem; | |
71 | setDate(aDate); | |
72 | } | |
73 | ||
74 | public Object getSelectedItem() { | |
75 | return calendar.getTime(); | |
76 | } | |
77 | ||
78 | public int getSize() { | |
79 | return calendar.getActualMaximum(Calendar.MONTH) + 1; | |
80 | } | |
81 | ||
82 | public Object getElementAt(int index) { | |
83 | Calendar c = Calendar.getInstance(locale); | |
84 | c.setTime(calendar.getTime()); | |
85 | ||
86 | c.set(Calendar.MONTH, 0); | |
87 | for (int i = 0; i < index; i++) | |
88 | c.add(Calendar.MONTH, 1); | |
89 | ||
90 | return c.getTime(); | |
91 | } | |
92 | ||
93 | public TimeZone getZone() { | |
94 | return zone; | |
95 | } | |
96 | ||
97 | public void setZone(TimeZone zone) { | |
98 | this.zone = zone; | |
99 | createLocaleAndZoneSensitive(); | |
100 | fireContentsChanged(this, 0, getSize() - 1); | |
101 | } | |
102 | ||
103 | public void addPropertyChangeListener(PropertyChangeListener listener) { | |
104 | changeSupport.addPropertyChangeListener(listener); | |
105 | } | |
106 | ||
107 | public void addPropertyChangeListener(String propertyName, | |
108 | PropertyChangeListener listener) { | |
109 | changeSupport.addPropertyChangeListener(propertyName, listener); | |
110 | } | |
111 | ||
112 | public PropertyChangeListener[] getPropertyChangeListeners() { | |
113 | return changeSupport.getPropertyChangeListeners(); | |
114 | } | |
115 | ||
116 | public PropertyChangeListener[] getPropertyChangeListeners( | |
117 | String propertyName) { | |
118 | return changeSupport.getPropertyChangeListeners(propertyName); | |
119 | } | |
120 | ||
121 | public boolean hasListeners(String propertyName) { | |
122 | return changeSupport.hasListeners(propertyName); | |
123 | } | |
124 | ||
125 | public void removePropertyChangeListener(PropertyChangeListener listener) { | |
126 | changeSupport.removePropertyChangeListener(listener); | |
127 | } | |
128 | ||
129 | public void removePropertyChangeListener(String propertyName, | |
130 | PropertyChangeListener listener) { | |
131 | changeSupport.removePropertyChangeListener(propertyName, listener); | |
132 | } | |
133 | ||
134 | } |
+57
-0
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.awt.Component; | |
3 | import java.text.SimpleDateFormat; | |
4 | import java.util.Date; | |
5 | import java.util.Locale; | |
6 | import java.util.TimeZone; | |
7 | ||
8 | import javax.swing.DefaultListCellRenderer; | |
9 | import javax.swing.JList; | |
10 | ||
11 | class MonthComboBoxRenderer extends DefaultListCellRenderer { | |
12 | ||
13 | // private Locale locale; | |
14 | ||
15 | private TimeZone zone; | |
16 | ||
17 | private SimpleDateFormat dateFormat; | |
18 | ||
19 | public MonthComboBoxRenderer(Locale locale, TimeZone zone) { | |
20 | // this.locale = locale; | |
21 | this.zone = zone; | |
22 | dateFormat = new SimpleDateFormat("MMMM", locale); | |
23 | dateFormat.setTimeZone(zone); | |
24 | } | |
25 | ||
26 | public Component getListCellRendererComponent(JList list, Object value, | |
27 | int index, boolean isSelected, boolean cellHasFocus) { | |
28 | super.getListCellRendererComponent(list, value, index, isSelected, | |
29 | cellHasFocus); | |
30 | ||
31 | Date date = (Date) value; | |
32 | setText(dateFormat.format(date)); | |
33 | ||
34 | return this; | |
35 | } | |
36 | ||
37 | public void setLocale(Locale locale) { | |
38 | // this.locale = locale; | |
39 | dateFormat = new SimpleDateFormat("MMMM", locale); | |
40 | dateFormat.setTimeZone(zone); | |
41 | } | |
42 | ||
43 | public void setZone(TimeZone zone) { | |
44 | this.zone = zone; | |
45 | dateFormat.setTimeZone(zone); | |
46 | } | |
47 | ||
48 | // public Locale getLocale() { | |
49 | // return locale; | |
50 | // } | |
51 | // | |
52 | // public TimeZone getZone() { | |
53 | // return zone; | |
54 | // } | |
55 | ||
56 | } |
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.text.NumberFormat; | |
3 | ||
4 | import javax.swing.JComponent; | |
5 | import javax.swing.JFormattedTextField; | |
6 | import javax.swing.JSpinner; | |
7 | import javax.swing.SpinnerModel; | |
8 | import javax.swing.SpinnerNumberModel; | |
9 | import javax.swing.text.DefaultFormatterFactory; | |
10 | import javax.swing.text.NumberFormatter; | |
11 | ||
12 | class NoGroupingSpinner extends JSpinner { | |
13 | ||
14 | public static class NoGroupingNumberEditor extends NumberEditor { | |
15 | ||
16 | public NoGroupingNumberEditor(JSpinner spinner, SpinnerModel model) { | |
17 | super(spinner); | |
18 | JFormattedTextField ftf = (JFormattedTextField) this | |
19 | .getComponent(0); | |
20 | NumberFormat fmt = NumberFormat.getIntegerInstance(); | |
21 | fmt.setGroupingUsed(false); | |
22 | ftf.setFormatterFactory(new DefaultFormatterFactory( | |
23 | new NumberFormatter(fmt))); | |
24 | revalidate(); | |
25 | } | |
26 | ||
27 | } | |
28 | ||
29 | public NoGroupingSpinner(SpinnerModel spinnerModel) { | |
30 | super(spinnerModel); | |
31 | } | |
32 | ||
33 | protected JComponent createEditor(SpinnerModel model) { | |
34 | if (model instanceof SpinnerNumberModel) | |
35 | return new NoGroupingNumberEditor(this, model); | |
36 | ||
37 | return super.createEditor(model); | |
38 | } | |
39 | ||
40 | } |
0 | package com.michaelbaranov.microba.calendar.ui.basic; | |
1 | ||
2 | import java.beans.PropertyChangeListener; | |
3 | import java.beans.PropertyChangeSupport; | |
4 | import java.util.Calendar; | |
5 | import java.util.Date; | |
6 | import java.util.Locale; | |
7 | import java.util.TimeZone; | |
8 | ||
9 | import javax.swing.SpinnerNumberModel; | |
10 | ||
11 | class YearSpinnerModel extends SpinnerNumberModel { | |
12 | ||
13 | public static final String PROPERTY_NAME_LOCALE = "locale"; | |
14 | ||
15 | public static final String PROPERTY_NAME_DATE = "date"; | |
16 | ||
17 | public static final String PROPERTY_NAME_ZONE = "zone"; | |
18 | ||
19 | private PropertyChangeSupport changeSupport = new PropertyChangeSupport( | |
20 | this); | |
21 | ||
22 | private Locale locale; | |
23 | ||
24 | private TimeZone zone; | |
25 | ||
26 | private Calendar calendar; | |
27 | ||
28 | public YearSpinnerModel(Date date, Locale locale, TimeZone zone) { | |
29 | this.locale = locale; | |
30 | this.zone = zone; | |
31 | createLocaleAndZoneSensitive(); | |
32 | calendar.setTime(date); | |
33 | } | |
34 | ||
35 | private void createLocaleAndZoneSensitive() { | |
36 | if (calendar != null) { | |
37 | Date old = calendar.getTime(); | |
38 | calendar = Calendar.getInstance(zone, locale); | |
39 | calendar.setTime(old); | |
40 | } else | |
41 | calendar = Calendar.getInstance(zone, locale); | |
42 | } | |
43 | ||
44 | public Object getValue() { | |
45 | return new Integer(calendar.get(Calendar.YEAR)); | |
46 | } | |
47 | ||
48 | public void setValue(Object value) { | |
49 | Number newVal = (Number) value; | |
50 | Number oldVal = (Number) getValue(); | |
51 | if (oldVal.longValue() != newVal.longValue()) { | |
52 | ||
53 | int diff = newVal.intValue() - oldVal.intValue(); | |
54 | int sign = diff > 0 ? 1 : -1; | |
55 | if (diff < 0) | |
56 | diff = -diff; | |
57 | Date oldDate = calendar.getTime(); | |
58 | ||
59 | for (int i = 0; i < diff; i++) | |
60 | calendar.add(Calendar.YEAR, sign); | |
61 | ||
62 | changeSupport.firePropertyChange(PROPERTY_NAME_DATE, oldDate, | |
63 | getDate()); | |
64 | fireStateChanged(); | |
65 | } | |
66 | } | |
67 | ||
68 | public Object getNextValue() { | |
69 | ||
70 | Integer currVal = (Integer) getValue(); | |
71 | int newVal = currVal.intValue() + 1; | |
72 | ||
73 | if (newVal <= calendar.getActualMaximum(Calendar.YEAR)) | |
74 | return new Integer(newVal); | |
75 | ||
76 | return currVal; | |
77 | } | |
78 | ||
79 | public Object getPreviousValue() { | |
80 | Integer currVal = (Integer) getValue(); | |
81 | int newVal = currVal.intValue() - 1; | |
82 | ||
83 | if (newVal >= calendar.getActualMinimum(Calendar.YEAR)) | |
84 | return new Integer(newVal); | |
85 | ||
86 | return currVal; | |
87 | } | |
88 | ||
89 | public Date getDate() { | |
90 | return calendar.getTime(); | |
91 | } | |
92 | ||
93 | public void setDate(Date date) { | |
94 | Date old = calendar.getTime(); | |
95 | if (!old.equals(date)) { | |
96 | calendar.setTime(date); | |
97 | changeSupport.firePropertyChange(PROPERTY_NAME_DATE, old, date); | |
98 | fireStateChanged(); | |
99 | } | |
100 | } | |
101 | ||
102 | public Locale getLocale() { | |
103 | return locale; | |
104 | } | |
105 | ||
106 | public void setLocale(Locale locale) { | |
107 | Locale old = this.locale; | |
108 | this.locale = locale; | |
109 | createLocaleAndZoneSensitive(); | |
110 | changeSupport.firePropertyChange(PROPERTY_NAME_LOCALE, old, locale); | |
111 | fireStateChanged(); | |
112 | } | |
113 | ||
114 | public TimeZone getZone() { | |
115 | return zone; | |
116 | } | |
117 | ||
118 | public void setZone(TimeZone zone) { | |
119 | TimeZone old = this.zone; | |
120 | this.zone = zone; | |
121 | createLocaleAndZoneSensitive(); | |
122 | changeSupport.firePropertyChange(PROPERTY_NAME_LOCALE, old, locale); | |
123 | fireStateChanged(); | |
124 | } | |
125 | ||
126 | public void addPropertyChangeListener(PropertyChangeListener listener) { | |
127 | changeSupport.addPropertyChangeListener(listener); | |
128 | } | |
129 | ||
130 | public void addPropertyChangeListener(String propertyName, | |
131 | PropertyChangeListener listener) { | |
132 | changeSupport.addPropertyChangeListener(propertyName, listener); | |
133 | } | |
134 | ||
135 | public void removePropertyChangeListener(PropertyChangeListener listener) { | |
136 | changeSupport.removePropertyChangeListener(listener); | |
137 | } | |
138 | ||
139 | public void removePropertyChangeListener(String propertyName, | |
140 | PropertyChangeListener listener) { | |
141 | changeSupport.removePropertyChangeListener(propertyName, listener); | |
142 | } | |
143 | ||
144 | public PropertyChangeListener[] getPropertyChangeListeners() { | |
145 | return changeSupport.getPropertyChangeListeners(); | |
146 | } | |
147 | ||
148 | public PropertyChangeListener[] getPropertyChangeListeners( | |
149 | String propertyName) { | |
150 | return changeSupport.getPropertyChangeListeners(propertyName); | |
151 | } | |
152 | ||
153 | public boolean hasListeners(String propertyName) { | |
154 | return changeSupport.hasListeners(propertyName); | |
155 | } | |
156 | ||
157 | } |
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | import java.beans.PropertyChangeEvent; | |
3 | import java.beans.PropertyChangeListener; | |
4 | import java.beans.PropertyChangeSupport; | |
5 | ||
6 | import javax.swing.table.AbstractTableModel; | |
7 | ||
8 | /** | |
9 | * A simple abstract implementation of <code>BoundedTableModel</code>. A | |
10 | * convenience class. | |
11 | * | |
12 | * @author Michael Baranov | |
13 | */ | |
14 | public abstract class AbstractBoundedTableModel extends AbstractTableModel | |
15 | implements BoundedTableModel { | |
16 | ||
17 | private PropertyChangeSupport propertySupport=new PropertyChangeSupport(this); | |
18 | ||
19 | public void addPropertyChangeListener(PropertyChangeListener listener) { | |
20 | propertySupport.addPropertyChangeListener(listener); | |
21 | } | |
22 | ||
23 | public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { | |
24 | propertySupport.addPropertyChangeListener(propertyName, listener); | |
25 | } | |
26 | ||
27 | public void firePropertyChange(PropertyChangeEvent evt) { | |
28 | propertySupport.firePropertyChange(evt); | |
29 | } | |
30 | ||
31 | public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { | |
32 | propertySupport.firePropertyChange(propertyName, oldValue, newValue); | |
33 | } | |
34 | ||
35 | public void firePropertyChange(String propertyName, int oldValue, int newValue) { | |
36 | propertySupport.firePropertyChange(propertyName, oldValue, newValue); | |
37 | } | |
38 | ||
39 | public void firePropertyChange(String propertyName, Object oldValue, Object newValue) { | |
40 | propertySupport.firePropertyChange(propertyName, oldValue, newValue); | |
41 | } | |
42 | ||
43 | public PropertyChangeListener[] getPropertyChangeListeners() { | |
44 | return propertySupport.getPropertyChangeListeners(); | |
45 | } | |
46 | ||
47 | public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { | |
48 | return propertySupport.getPropertyChangeListeners(propertyName); | |
49 | } | |
50 | ||
51 | public boolean hasListeners(String propertyName) { | |
52 | return propertySupport.hasListeners(propertyName); | |
53 | } | |
54 | ||
55 | public void removePropertyChangeListener(PropertyChangeListener listener) { | |
56 | propertySupport.removePropertyChangeListener(listener); | |
57 | } | |
58 | ||
59 | public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { | |
60 | propertySupport.removePropertyChangeListener(propertyName, listener); | |
61 | } | |
62 | ||
63 | } |
+128
-0
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | import java.util.EventListener; | |
3 | ||
4 | import javax.swing.DefaultListSelectionModel; | |
5 | import javax.swing.ListSelectionModel; | |
6 | import javax.swing.event.ListSelectionListener; | |
7 | ||
8 | /** | |
9 | * A simple abstract implementation of <code>BoundedTableModel</code> with | |
10 | * implemented <code>ListSelectionModel</code> functionality. A convenience | |
11 | * class. | |
12 | * | |
13 | * @author Michael Baranov | |
14 | * | |
15 | */ | |
16 | public abstract class AbstractBoundedTableModelWithSelection extends | |
17 | AbstractBoundedTableModel implements ListSelectionModel { | |
18 | ||
19 | private DefaultListSelectionModel selection = new DefaultListSelectionModel(); | |
20 | ||
21 | public AbstractBoundedTableModelWithSelection() { | |
22 | super(); | |
23 | } | |
24 | ||
25 | public void addListSelectionListener(ListSelectionListener l) { | |
26 | selection.addListSelectionListener(l); | |
27 | } | |
28 | ||
29 | public void addSelectionInterval(int index0, int index1) { | |
30 | selection.addSelectionInterval(index0, index1); | |
31 | } | |
32 | ||
33 | public void clearSelection() { | |
34 | selection.clearSelection(); | |
35 | } | |
36 | ||
37 | public int getAnchorSelectionIndex() { | |
38 | return selection.getAnchorSelectionIndex(); | |
39 | } | |
40 | ||
41 | public int getLeadSelectionIndex() { | |
42 | return selection.getLeadSelectionIndex(); | |
43 | } | |
44 | ||
45 | public EventListener[] getListeners(Class listenerType) { | |
46 | return selection.getListeners(listenerType); | |
47 | } | |
48 | ||
49 | public ListSelectionListener[] getListSelectionListeners() { | |
50 | return selection.getListSelectionListeners(); | |
51 | } | |
52 | ||
53 | public int getMaxSelectionIndex() { | |
54 | return selection.getMaxSelectionIndex(); | |
55 | } | |
56 | ||
57 | public int getMinSelectionIndex() { | |
58 | return selection.getMinSelectionIndex(); | |
59 | } | |
60 | ||
61 | public int getSelectionMode() { | |
62 | return selection.getSelectionMode(); | |
63 | } | |
64 | ||
65 | public boolean getValueIsAdjusting() { | |
66 | return selection.getValueIsAdjusting(); | |
67 | } | |
68 | ||
69 | public void insertIndexInterval(int index, int length, boolean before) { | |
70 | selection.insertIndexInterval(index, length, before); | |
71 | } | |
72 | ||
73 | public boolean isLeadAnchorNotificationEnabled() { | |
74 | return selection.isLeadAnchorNotificationEnabled(); | |
75 | } | |
76 | ||
77 | public boolean isSelectedIndex(int index) { | |
78 | return selection.isSelectedIndex(index); | |
79 | } | |
80 | ||
81 | public boolean isSelectionEmpty() { | |
82 | return selection.isSelectionEmpty(); | |
83 | } | |
84 | ||
85 | // J2SE5 | |
86 | // //////////////////////////////////////////////////// | |
87 | // public void moveLeadSelectionIndex(int leadIndex) { | |
88 | // selection.moveLeadSelectionIndex(leadIndex); | |
89 | // } | |
90 | ||
91 | public void removeIndexInterval(int index0, int index1) { | |
92 | selection.removeIndexInterval(index0, index1); | |
93 | } | |
94 | ||
95 | public void removeListSelectionListener(ListSelectionListener l) { | |
96 | selection.removeListSelectionListener(l); | |
97 | } | |
98 | ||
99 | public void removeSelectionInterval(int index0, int index1) { | |
100 | selection.removeSelectionInterval(index0, index1); | |
101 | } | |
102 | ||
103 | public void setAnchorSelectionIndex(int anchorIndex) { | |
104 | selection.setAnchorSelectionIndex(anchorIndex); | |
105 | } | |
106 | ||
107 | public void setLeadAnchorNotificationEnabled(boolean flag) { | |
108 | selection.setLeadAnchorNotificationEnabled(flag); | |
109 | } | |
110 | ||
111 | public void setLeadSelectionIndex(int leadIndex) { | |
112 | selection.setLeadSelectionIndex(leadIndex); | |
113 | } | |
114 | ||
115 | public void setSelectionInterval(int index0, int index1) { | |
116 | selection.setSelectionInterval(index0, index1); | |
117 | } | |
118 | ||
119 | public void setSelectionMode(int selectionMode) { | |
120 | selection.setSelectionMode(selectionMode); | |
121 | } | |
122 | ||
123 | public void setValueIsAdjusting(boolean isAdjusting) { | |
124 | selection.setValueIsAdjusting(isAdjusting); | |
125 | } | |
126 | ||
127 | } |
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | import javax.swing.event.EventListenerList; | |
3 | ||
4 | ||
5 | /** | |
6 | * This is a convenience implementation of {@link Policy}. | |
7 | * | |
8 | * @author Michael Baranov | |
9 | * | |
10 | */ | |
11 | public abstract class AbstractPolicy implements Policy { | |
12 | private EventListenerList vetoPolicyListenerList = new EventListenerList(); | |
13 | ||
14 | public void addVetoPolicyListener(PolicyListener listener) { | |
15 | vetoPolicyListenerList.add(PolicyListener.class, listener); | |
16 | } | |
17 | ||
18 | public void removeVetoPolicyListener(PolicyListener listener) { | |
19 | vetoPolicyListenerList.remove(PolicyListener.class, listener); | |
20 | ||
21 | } | |
22 | ||
23 | /** | |
24 | * Fires a {@link PolicyEvent} to all related {@link PolicyListener}s. | |
25 | */ | |
26 | protected void fireVetoPolicyChangeAction() { | |
27 | Object[] listeners = vetoPolicyListenerList.getListenerList(); | |
28 | ||
29 | for (int i = listeners.length - 2; i >= 0; i -= 2) | |
30 | if (listeners[i] == PolicyListener.class) | |
31 | ((PolicyListener) listeners[i + 1]) | |
32 | .policyChanged(new PolicyEvent(this)); | |
33 | } | |
34 | ||
35 | } |
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | import java.beans.PropertyChangeListener; | |
3 | ||
4 | import javax.swing.table.TableModel; | |
5 | ||
6 | /** | |
7 | * An extended <code>TableModel</code>. | |
8 | * <p> | |
9 | * The upper and lower bound values are introduced to further describe table | |
10 | * data. For example, <code>BoundedTableModel</code> is used as a data model | |
11 | * for <code>{@link com.michaelbaranov.microba.marker.MarkerBar}</code>, | |
12 | * <code>{@link com.michaelbaranov.microba.gradient.GradientBar}</code>, | |
13 | * <code>{@link com.michaelbaranov.microba.gradienteditor.GradientEditor}</code> | |
14 | * | |
15 | * @version 0.1 (rev. 13 Aug 2005) | |
16 | * @author Michael Baranov <a | |
17 | * href=http://www.michaelbaranov.com>www.michaelbaranov.com</a> | |
18 | * | |
19 | */ | |
20 | public interface BoundedTableModel extends TableModel { | |
21 | ||
22 | /** | |
23 | * The name of the bound property, that holds lower bound value. | |
24 | */ | |
25 | public static final String PROPERTY_LOWER_BOUND = "lowerBound"; | |
26 | ||
27 | /** | |
28 | * The name of the bound property, that holds upper bound value. | |
29 | */ | |
30 | public static final String PROPERTY_UPPER_BOUND = "upperBound"; | |
31 | ||
32 | /** | |
33 | * Returns some lower bound, further describing the data. | |
34 | * | |
35 | * @return lower bound. | |
36 | */ | |
37 | int getLowerBound(); | |
38 | ||
39 | /** | |
40 | * Returns some upper bound, further describing the data. | |
41 | * | |
42 | * @return upper bound. | |
43 | */ | |
44 | int getUpperBound(); | |
45 | ||
46 | public void addPropertyChangeListener(PropertyChangeListener listener); | |
47 | ||
48 | public void addPropertyChangeListener(String propertyName, | |
49 | PropertyChangeListener listener); | |
50 | ||
51 | public void removePropertyChangeListener(PropertyChangeListener listener); | |
52 | ||
53 | public void removePropertyChangeListener(String propertyName, | |
54 | PropertyChangeListener listener); | |
55 | ||
56 | } |
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | import java.util.EventObject; | |
3 | ||
4 | /** | |
5 | * An event used to indicate a change to a control has been commited or reverted | |
6 | * (rolled back). | |
7 | * | |
8 | * @author Michael Baranov | |
9 | * | |
10 | */ | |
11 | public class CommitEvent extends EventObject { | |
12 | ||
13 | private boolean commit; | |
14 | ||
15 | /** | |
16 | * Constructor. | |
17 | * | |
18 | * @param source | |
19 | * a control that fired the event | |
20 | * @param commit | |
21 | * <code>true</code> to indicate commit, <code>false</code> | |
22 | * to indicate revert (rollback) | |
23 | */ | |
24 | public CommitEvent(Object source, boolean commit) { | |
25 | super(source); | |
26 | this.commit = commit; | |
27 | } | |
28 | ||
29 | /** | |
30 | * Returns the type of the event. | |
31 | * | |
32 | * @return <code>true</code> if a change has been commited to a control, | |
33 | * <code>false</code> otherwise. | |
34 | */ | |
35 | public boolean isCommit() { | |
36 | return commit; | |
37 | } | |
38 | ||
39 | } |
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | import java.util.EventListener; | |
3 | ||
4 | /** | |
5 | * A listener that is notified of {@link CommitEvent} events. | |
6 | * | |
7 | * @author Michael Baranov | |
8 | * | |
9 | */ | |
10 | public interface CommitListener extends EventListener { | |
11 | /** | |
12 | * Called when a {@link CommitEvent} is fired. | |
13 | * | |
14 | * @param event | |
15 | */ | |
16 | public void commit(CommitEvent event); | |
17 | ||
18 | } |
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.util.Collections; | |
4 | import java.util.Map; | |
5 | ||
6 | import javax.swing.JComponent; | |
7 | import javax.swing.UIManager; | |
8 | import javax.swing.plaf.ComponentUI; | |
9 | ||
10 | import com.michaelbaranov.microba.Microba; | |
11 | ||
12 | /** | |
13 | * Superclass for all Microba GUI components. | |
14 | * | |
15 | * @author Michael Baranov | |
16 | * | |
17 | */ | |
18 | public class MicrobaComponent extends JComponent { | |
19 | ||
20 | public static final String PROPERTY_NAME_COLOR_OVERRIDE_MAP = "colorOverrideMap"; | |
21 | ||
22 | static { | |
23 | Microba.init(); | |
24 | } | |
25 | ||
26 | protected Map colorOverrideMap; | |
27 | ||
28 | public ComponentUI getUI() { | |
29 | return ui; | |
30 | } | |
31 | ||
32 | /** | |
33 | * Sets the UI delegate of this component to the corresponding UI delegate | |
34 | * taken from UIManager. | |
35 | * <p> | |
36 | * This implementation has a workarount to fix the problem with non-standard | |
37 | * class-loaders. | |
38 | */ | |
39 | public void updateUI() { | |
40 | UIManager.getDefaults().put(UIManager.get(this.getUIClassID()), null); | |
41 | ComponentUI delegate = UIManager.getUI(this); | |
42 | ||
43 | setUI(delegate); | |
44 | invalidate(); | |
45 | } | |
46 | ||
47 | /** | |
48 | * Returns per-instance (only for this instance) map of color overrides. May | |
49 | * be <code>null</code>. | |
50 | * <p> | |
51 | * NOTE: returned map is unmodifiable. Use {@link #setColorOverrideMap(Map)} | |
52 | * to change the map. | |
53 | * | |
54 | * @return keys in the map are {@link String} constants, valuse are of type | |
55 | * {@link Color} or of type {@link String} (in this case, | |
56 | * {@link Color} values are obtained via | |
57 | * {@link UIManager#getColor(Object)}) | |
58 | */ | |
59 | public Map getColorOverrideMap() { | |
60 | if (colorOverrideMap == null) | |
61 | return null; | |
62 | ||
63 | return Collections.unmodifiableMap(colorOverrideMap); | |
64 | } | |
65 | ||
66 | /** | |
67 | * Sets per-instance (only for this instance) map of color overrides. | |
68 | * | |
69 | * @param colorOverrideMap | |
70 | * keys in the map are {@link String} constants, valuse are of | |
71 | * type {@link Color} or of type {@link String} (in this case, | |
72 | * {@link Color} values are obtained via | |
73 | * {@link UIManager#getColor(Object)}). May be <code>null</code>. | |
74 | */ | |
75 | public void setColorOverrideMap(Map colorOverrideMap) { | |
76 | Object old = this.colorOverrideMap; | |
77 | this.colorOverrideMap = colorOverrideMap; | |
78 | firePropertyChange(PROPERTY_NAME_COLOR_OVERRIDE_MAP, old, | |
79 | colorOverrideMap); | |
80 | ||
81 | updateUI(); | |
82 | } | |
83 | ||
84 | } |
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | public interface Policy { | |
3 | /** | |
4 | * Adds a {@link PolicyListener}. | |
5 | * | |
6 | * @param listener | |
7 | * a {@link PolicyListener} to add | |
8 | */ | |
9 | public void addVetoPolicyListener(PolicyListener listener); | |
10 | ||
11 | /** | |
12 | * Removes a {@link PolicyListener}. | |
13 | * | |
14 | * @param listener | |
15 | * a {@link PolicyListener} to remove | |
16 | */ | |
17 | public void removeVetoPolicyListener(PolicyListener listener); | |
18 | } |
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | import java.util.EventObject; | |
3 | ||
4 | /** | |
5 | * An event used to indicate a policy (algorithm) has changed. | |
6 | * | |
7 | * @author Michael Baranov | |
8 | * | |
9 | */ | |
10 | public class PolicyEvent extends EventObject { | |
11 | ||
12 | /** | |
13 | * Constructor. | |
14 | * | |
15 | * @param source | |
16 | * an object whose policy has changed | |
17 | */ | |
18 | public PolicyEvent(Object source) { | |
19 | super(source); | |
20 | } | |
21 | ||
22 | } |
0 | package com.michaelbaranov.microba.common; | |
1 | ||
2 | import java.util.EventListener; | |
3 | ||
4 | /** | |
5 | * A listener that is notified of {@link PolicyEvent} events. | |
6 | * | |
7 | * @author Michael Baranov | |
8 | * | |
9 | */ | |
10 | public interface PolicyListener extends EventListener { | |
11 | ||
12 | /** | |
13 | * Called when a {@link PolicyEvent} is fired. | |
14 | * | |
15 | * @param event | |
16 | */ | |
17 | public void policyChanged(PolicyEvent event); | |
18 | } |
0 | package com.michaelbaranov.microba.demo; | |
1 | ||
2 | import java.awt.BorderLayout; | |
3 | import java.awt.Color; | |
4 | import java.awt.GridBagConstraints; | |
5 | import java.awt.GridBagLayout; | |
6 | import java.awt.Insets; | |
7 | import java.awt.event.ActionEvent; | |
8 | import java.awt.event.ActionListener; | |
9 | import java.awt.event.MouseAdapter; | |
10 | import java.awt.event.MouseEvent; | |
11 | import java.awt.event.MouseListener; | |
12 | import java.beans.PropertyVetoException; | |
13 | import java.text.SimpleDateFormat; | |
14 | import java.util.Calendar; | |
15 | import java.util.Date; | |
16 | import java.util.HashMap; | |
17 | import java.util.Map; | |
18 | ||
19 | import javax.swing.JApplet; | |
20 | import javax.swing.JColorChooser; | |
21 | import javax.swing.JComponent; | |
22 | import javax.swing.JFrame; | |
23 | import javax.swing.JPanel; | |
24 | import javax.swing.JScrollPane; | |
25 | import javax.swing.JTabbedPane; | |
26 | import javax.swing.JTable; | |
27 | import javax.swing.SwingConstants; | |
28 | import javax.swing.event.ChangeEvent; | |
29 | import javax.swing.event.ChangeListener; | |
30 | import javax.swing.event.ListSelectionEvent; | |
31 | import javax.swing.event.ListSelectionListener; | |
32 | ||
33 | import com.michaelbaranov.microba.calendar.CalendarPane; | |
34 | import com.michaelbaranov.microba.calendar.DatePicker; | |
35 | import com.michaelbaranov.microba.calendar.DatePickerCellEditor; | |
36 | import com.michaelbaranov.microba.calendar.HolidayPolicy; | |
37 | import com.michaelbaranov.microba.common.AbstractPolicy; | |
38 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
39 | import com.michaelbaranov.microba.common.PolicyListener; | |
40 | import com.michaelbaranov.microba.gradient.GradientBar; | |
41 | import com.michaelbaranov.microba.gradienteditor.GradientEditor; | |
42 | import com.michaelbaranov.microba.marker.MarkerBar; | |
43 | ||
44 | public class Demo extends JApplet { | |
45 | ||
46 | static MarkerBar bar; | |
47 | ||
48 | static BoundedTableModel model; | |
49 | ||
50 | private static JFrame frame; | |
51 | ||
52 | private static JPanel panel; | |
53 | ||
54 | public static void main(String[] s) { | |
55 | ||
56 | Demo demo = new Demo(); | |
57 | demo.run(); | |
58 | ||
59 | } | |
60 | ||
61 | private void run() { | |
62 | frame = new JFrame(); | |
63 | frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); | |
64 | frame.getContentPane().setLayout(new BorderLayout()); | |
65 | ||
66 | JTabbedPane tabs = new JTabbedPane(); | |
67 | tabs.addTab("CalendarPane", buildCalendarPaneTab()); | |
68 | tabs.addTab("Gradient", buildGradientTab()); | |
69 | tabs.addTab("CellEditor", buildCellEditorTab()); | |
70 | tabs.addTab("DatePicker", buildDatePickerTab()); | |
71 | ||
72 | frame.getContentPane().add(tabs, BorderLayout.CENTER); | |
73 | frame.setSize(500, 300); | |
74 | frame.setVisible(true); | |
75 | ||
76 | } | |
77 | ||
78 | // | |
79 | // public void init() { | |
80 | // super.init(); | |
81 | // System.out.println("init"); | |
82 | // panel = new JPanel(); | |
83 | // panel.setLayout(new BorderLayout()); | |
84 | // | |
85 | // JTabbedPane tabs = new JTabbedPane(); | |
86 | // tabs.addTab("CalendarPane", buildCalendarPaneTab()); | |
87 | // tabs.addTab("Gradient", buildGradientTab()); | |
88 | // tabs.addTab("CellEditor", buildCellEditorTab()); | |
89 | // tabs.addTab("DatePicker", buildDatePickerTab()); | |
90 | // | |
91 | // panel.add(tabs, BorderLayout.CENTER); | |
92 | // this.setContentPane(panel); | |
93 | // } | |
94 | ||
95 | public void start() { | |
96 | System.out.println("start"); | |
97 | super.start(); | |
98 | } | |
99 | ||
100 | public void stop() { | |
101 | System.out.println("stop"); | |
102 | super.stop(); | |
103 | } | |
104 | ||
105 | private class Hol extends AbstractPolicy implements HolidayPolicy { | |
106 | ||
107 | public String getHollidayName(Object source, Calendar date) { | |
108 | return null; | |
109 | } | |
110 | ||
111 | public boolean isHolliday(Object source, Calendar date) { | |
112 | return false; | |
113 | } | |
114 | ||
115 | public boolean isWeekend(Object source, Calendar date) { | |
116 | return date.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY; | |
117 | } | |
118 | ||
119 | } | |
120 | ||
121 | private JComponent buildCalendarPaneTab() { | |
122 | JPanel panel = new JPanel(); | |
123 | final CalendarPane calendarPane = new CalendarPane(); | |
124 | ||
125 | // calendarPane.setEnabled(false); | |
126 | calendarPane.setHolidayPolicy(new Hol()); | |
127 | ||
128 | Map ov = new HashMap(); | |
129 | ||
130 | ov.put(CalendarPane.COLOR_CALENDAR_GRID_FOREGROUND_ENABLED, | |
131 | Color.ORANGE); | |
132 | ||
133 | calendarPane.setColorOverrideMap(ov); | |
134 | ||
135 | try { | |
136 | calendarPane.setDate(new Date()); | |
137 | } catch (PropertyVetoException e) { | |
138 | // TODO Auto-generated catch block | |
139 | e.printStackTrace(); | |
140 | } | |
141 | ||
142 | calendarPane.addActionListener(new ActionListener() { | |
143 | ||
144 | public void actionPerformed(ActionEvent e) { | |
145 | System.out.println("CalendarPane:" + calendarPane.getDate()); | |
146 | ||
147 | } | |
148 | }); | |
149 | ||
150 | panel.setLayout(new GridBagLayout()); | |
151 | panel.add(calendarPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, | |
152 | GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( | |
153 | 5, 5, 5, 5), 0, 0)); | |
154 | ||
155 | panel.addMouseListener(new MouseAdapter() { | |
156 | public void mouseClicked(MouseEvent e) { | |
157 | calendarPane.observeMonth(2009, 4); | |
158 | } | |
159 | }); | |
160 | ||
161 | return panel; | |
162 | ||
163 | } | |
164 | ||
165 | private JComponent buildDatePickerTab() { | |
166 | JPanel panel = new JPanel(); | |
167 | final DatePicker datePicker = new DatePicker(); | |
168 | // datePicker.setDateFormat(new SimpleDateFormat("HH dd yyyy")); | |
169 | datePicker.setDateFormat(SimpleDateFormat.getDateTimeInstance()); | |
170 | // datePicker.setStripTime(false); | |
171 | datePicker.setEnabled(false); | |
172 | datePicker.setKeepTime(true); | |
173 | datePicker.setStripTime(false); | |
174 | datePicker.setShowNumberOfWeek(true); | |
175 | // datePicker.setEnabled(false); | |
176 | // datePicker.setPickerStyle(DatePicker.PICKER_STYLE_BUTTON); | |
177 | // datePicker.showButtonOnly(false); | |
178 | // datePicker.setToolTipText("hello!!!!"); | |
179 | // datePicker.setShowNumberOfWeek(true); | |
180 | ||
181 | Map ov = new HashMap(); | |
182 | ||
183 | ov.put(CalendarPane.COLOR_CALENDAR_GRID_FOREGROUND_ENABLED, | |
184 | Color.ORANGE); | |
185 | ||
186 | datePicker.setColorOverrideMap(ov); | |
187 | ||
188 | panel.setLayout(new GridBagLayout()); | |
189 | panel.add(datePicker, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, | |
190 | GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( | |
191 | 5, 5, 5, 5), 0, 0)); | |
192 | ||
193 | datePicker.addActionListener(new ActionListener() { | |
194 | ||
195 | public void actionPerformed(ActionEvent e) { | |
196 | System.out.println("DatePicker:" + datePicker.getDate()); | |
197 | ||
198 | } | |
199 | }); | |
200 | ||
201 | panel.addMouseListener(new MouseListener() { | |
202 | ||
203 | public void mouseClicked(MouseEvent e) { | |
204 | try { | |
205 | datePicker.setDate(null); | |
206 | } catch (PropertyVetoException e1) { | |
207 | // TODO Auto-generated catch block | |
208 | e1.printStackTrace(); | |
209 | } | |
210 | ||
211 | } | |
212 | ||
213 | public void mouseEntered(MouseEvent e) { | |
214 | // TODO Auto-generated method stub | |
215 | ||
216 | } | |
217 | ||
218 | public void mouseExited(MouseEvent e) { | |
219 | // TODO Auto-generated method stub | |
220 | ||
221 | } | |
222 | ||
223 | public void mousePressed(MouseEvent e) { | |
224 | // TODO Auto-generated method stub | |
225 | ||
226 | } | |
227 | ||
228 | public void mouseReleased(MouseEvent e) { | |
229 | // TODO Auto-generated method stub | |
230 | ||
231 | } | |
232 | }); | |
233 | ||
234 | return panel; | |
235 | ||
236 | } | |
237 | ||
238 | private JComponent buildCellEditorTab() { | |
239 | JPanel panel = new JPanel(); | |
240 | DatePicker datePicker = new DatePicker(); | |
241 | ||
242 | datePicker.setDropdownFocusable(true); | |
243 | ||
244 | DatePickerCellEditor cellEditor = new DatePickerCellEditor(datePicker); | |
245 | cellEditor.setClickCountToStart(2); | |
246 | ||
247 | JTable table = new JTable(100, 3); | |
248 | table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); | |
249 | JScrollPane scrollPane = new JScrollPane(table); | |
250 | ||
251 | table.getColumnModel().getColumn(0).setCellEditor(cellEditor); | |
252 | ||
253 | panel.setLayout(new GridBagLayout()); | |
254 | panel.add(scrollPane, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, | |
255 | GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( | |
256 | 5, 5, 5, 5), 0, 0)); | |
257 | return panel; | |
258 | ||
259 | } | |
260 | ||
261 | private JComponent buildGradientTab() { | |
262 | JPanel panel = new JPanel(); | |
263 | GradientBar gradient; | |
264 | ||
265 | GradientEditor editor; | |
266 | JColorChooser colorChooser; | |
267 | ||
268 | colorChooser = new JColorChooser(); | |
269 | editor = new GradientEditor(); | |
270 | ||
271 | // editor.setOrientation(SwingConstants.VERTICAL); | |
272 | ||
273 | ColorAdaptor adaptor = new ColorAdaptor(editor, colorChooser); | |
274 | ||
275 | panel.add(editor, BorderLayout.NORTH); | |
276 | panel.add(colorChooser, BorderLayout.CENTER); | |
277 | ||
278 | MarkerBar bar = new MarkerBar(); | |
279 | bar.setDataModel(editor.getDataModel()); | |
280 | bar.setPositionColumn(editor.getColorPositionColumn()); | |
281 | bar.setColorColumn(editor.getColorColumn()); | |
282 | bar.setFliped(true); | |
283 | bar.setOrientation(SwingConstants.VERTICAL); | |
284 | panel.add(bar, BorderLayout.EAST); | |
285 | return panel; | |
286 | } | |
287 | ||
288 | private static class ColorAdaptor implements ChangeListener, | |
289 | ListSelectionListener { | |
290 | ||
291 | private GradientEditor editor; | |
292 | ||
293 | private JColorChooser chooser; | |
294 | ||
295 | public ColorAdaptor(GradientEditor editor, JColorChooser chooser) { | |
296 | super(); | |
297 | this.editor = editor; | |
298 | this.chooser = chooser; | |
299 | ||
300 | editor.getColorSelectionModel().addListSelectionListener(this); | |
301 | chooser.getSelectionModel().addChangeListener(this); | |
302 | } | |
303 | ||
304 | public void valueChanged(ListSelectionEvent e) { | |
305 | int index = editor.getColorSelectionModel().getLeadSelectionIndex(); | |
306 | // System.out.println(index); | |
307 | // System.out.println(e.getFirstIndex()); | |
308 | // System.out.println(e.getLastIndex()); | |
309 | // System.out.println("-------"); | |
310 | ||
311 | Color c = (Color) editor.getDataModel().getValueAt(index, | |
312 | editor.getColorColumn()); | |
313 | chooser.setColor(c); | |
314 | } | |
315 | ||
316 | public void stateChanged(ChangeEvent e) { | |
317 | ||
318 | if (!editor.getColorSelectionModel().isSelectionEmpty()) { | |
319 | int index = editor.getColorSelectionModel() | |
320 | .getLeadSelectionIndex(); | |
321 | ||
322 | editor.getDataModel().setValueAt(chooser.getColor(), index, | |
323 | editor.getColorColumn()); | |
324 | } | |
325 | ||
326 | } | |
327 | ||
328 | } | |
329 | ||
330 | } |
0 | package com.michaelbaranov.microba.gradient; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.util.ArrayList; | |
4 | import java.util.List; | |
5 | ||
6 | import com.michaelbaranov.microba.common.AbstractBoundedTableModel; | |
7 | ||
8 | /** | |
9 | * A very basic implementation of {@link AbstractBoundedTableModel} used by | |
10 | * default by {@link GradientBar}. This implementation has bounds 0 - 100 and | |
11 | * is mutable. | |
12 | * | |
13 | * @author Michael Baranov | |
14 | * | |
15 | */ | |
16 | public class DefaultGradientModel extends AbstractBoundedTableModel { | |
17 | ||
18 | protected static final int POSITION_COLUMN = 0; | |
19 | ||
20 | protected static final int COLOR_COLUMN = 1; | |
21 | ||
22 | protected List positionList = new ArrayList(32); | |
23 | ||
24 | protected List colorList = new ArrayList(32); | |
25 | ||
26 | /** | |
27 | * Constructor. | |
28 | */ | |
29 | public DefaultGradientModel() { | |
30 | super(); | |
31 | positionList.add(new Integer(0)); | |
32 | colorList.add(Color.YELLOW); | |
33 | ||
34 | positionList.add(new Integer(50)); | |
35 | colorList.add(Color.RED); | |
36 | ||
37 | positionList.add(new Integer(100)); | |
38 | colorList.add(Color.GREEN); | |
39 | } | |
40 | ||
41 | public int getLowerBound() { | |
42 | return 0; | |
43 | } | |
44 | ||
45 | public int getUpperBound() { | |
46 | return 100; | |
47 | } | |
48 | ||
49 | public int getRowCount() { | |
50 | return positionList.size(); | |
51 | } | |
52 | ||
53 | public int getColumnCount() { | |
54 | return 2; | |
55 | } | |
56 | ||
57 | public Class getColumnClass(int columnIndex) { | |
58 | switch (columnIndex) { | |
59 | case POSITION_COLUMN: | |
60 | return Integer.class; | |
61 | case COLOR_COLUMN: | |
62 | return Color.class; | |
63 | } | |
64 | return super.getColumnClass(columnIndex); | |
65 | } | |
66 | ||
67 | public Object getValueAt(int rowIndex, int columnIndex) { | |
68 | switch (columnIndex) { | |
69 | case POSITION_COLUMN: | |
70 | return positionList.get(rowIndex); | |
71 | case COLOR_COLUMN: | |
72 | return colorList.get(rowIndex); | |
73 | } | |
74 | return null; | |
75 | } | |
76 | ||
77 | /** | |
78 | * Adds a color point. | |
79 | * | |
80 | * @param color | |
81 | * @param position | |
82 | */ | |
83 | public void add(Color color, int position) { | |
84 | colorList.add(color); | |
85 | positionList.add(new Integer(position)); | |
86 | fireTableDataChanged(); | |
87 | } | |
88 | ||
89 | /** | |
90 | * Removes a color point at specified index. | |
91 | * | |
92 | * @param index | |
93 | */ | |
94 | public void remove(int index) { | |
95 | colorList.remove(index); | |
96 | positionList.remove(index); | |
97 | fireTableDataChanged(); | |
98 | } | |
99 | ||
100 | /** | |
101 | * Removes all color points. | |
102 | */ | |
103 | public void clear() { | |
104 | colorList.clear(); | |
105 | positionList.clear(); | |
106 | fireTableDataChanged(); | |
107 | } | |
108 | ||
109 | } |
0 | package com.michaelbaranov.microba.gradient; | |
1 | ||
2 | import java.awt.Color; | |
3 | ||
4 | import javax.swing.SwingConstants; | |
5 | ||
6 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
7 | import com.michaelbaranov.microba.common.MicrobaComponent; | |
8 | ||
9 | /** | |
10 | * IMPORTANT: alpha featre not implemented. Stubs only. | |
11 | * <p> | |
12 | * | |
13 | * A concrete implementation of JComponent. Displays an area filled with | |
14 | * gradient color (palette). The color and alpha value (transparency) is | |
15 | * linearly interpolated between key points. | |
16 | * <p> | |
17 | * The color key points are provided by the data model in combination with color | |
18 | * column (index) and color position column (index). Each row in the model | |
19 | * represents a colored key point. Color values are expected to be of type | |
20 | * {@link Color} and position values of type {@link Number} with integer | |
21 | * precision. | |
22 | * <p> | |
23 | * The alpha key points are provided by the alpha model in combination with | |
24 | * alpha column (index) and alpha position column (index). Each row in the model | |
25 | * represents an alpha key point. Alpha values are expected to be of type | |
26 | * {@link Number} with floating-point precision ranging form 0.0f (transparent) | |
27 | * to 1.0f (opaque) and position values of type {@link Number} with integer | |
28 | * precision. | |
29 | * | |
30 | * Example: | |
31 | * | |
32 | * <pre> | |
33 | * GradientBar bar = new GradientBar(); | |
34 | * bar.setDataModel(myColorModel); | |
35 | * bar.setColorColumn(0); | |
36 | * bar.setColorPositionColumn(1); | |
37 | * | |
38 | * bar.setAlphaModel(myAlphaModel); | |
39 | * bar.setAlphaColumn(1); | |
40 | * bar.setAlphaPositionColumn(0); | |
41 | * </pre> | |
42 | * | |
43 | * @author Michael Baranov | |
44 | * | |
45 | */ | |
46 | public class GradientBar extends MicrobaComponent { | |
47 | ||
48 | /** | |
49 | * The name of a "dataModel" property. | |
50 | */ | |
51 | public static final String PROPERTY_DATA_MODEL = "dataModel"; | |
52 | ||
53 | /** | |
54 | * The name of a "alphaModel" property. | |
55 | */ | |
56 | public static final String PROPERTY_ALPHA_MODEL = "alphaModel"; | |
57 | ||
58 | /** | |
59 | * The name of a "colorPositionColumn" property. | |
60 | */ | |
61 | public static final String PROPERTY_COLOR_POSITION_COLUMN = "colorPositionColumn"; | |
62 | ||
63 | /** | |
64 | * The name of a "alphaPositionColumn" property. | |
65 | */ | |
66 | public static final String PROPERTY_ALPHA_POSITION_COLUMN = "alphaPositionColumn"; | |
67 | ||
68 | /** | |
69 | * The name of a "colorColumn" property. | |
70 | */ | |
71 | public static final String PROPERTY_COLOR_COLUMN = "colorColumn"; | |
72 | ||
73 | /** | |
74 | * The name of a "alphaColumn" property. | |
75 | */ | |
76 | public static final String PROPERTY_ALPHA_COLUMN = "alphaColumn"; | |
77 | ||
78 | /** | |
79 | * The name of a "orientation" property. | |
80 | */ | |
81 | public static final String PROPERTY_ORIENTATION = "orientation"; | |
82 | ||
83 | private static final String uiClassID = "microba.GradientUI"; | |
84 | ||
85 | protected BoundedTableModel dataModel; | |
86 | ||
87 | protected BoundedTableModel alphaModel; | |
88 | ||
89 | protected int colorPositionColumn = 0; | |
90 | ||
91 | protected int alphaPositionColumn = 0; | |
92 | ||
93 | protected int colorColumn = 1; | |
94 | ||
95 | protected int alphaColumn = 1; | |
96 | ||
97 | protected int orientation = SwingConstants.HORIZONTAL; | |
98 | ||
99 | public String getUIClassID() { | |
100 | return uiClassID; | |
101 | } | |
102 | ||
103 | /** | |
104 | * Constructor. | |
105 | */ | |
106 | public GradientBar() { | |
107 | super(); | |
108 | dataModel = new DefaultGradientModel(); | |
109 | setFocusable(false); | |
110 | updateUI(); | |
111 | } | |
112 | ||
113 | /** | |
114 | * Constructor. | |
115 | */ | |
116 | public GradientBar(BoundedTableModel model) { | |
117 | super(); | |
118 | dataModel = model; | |
119 | setFocusable(false); | |
120 | updateUI(); | |
121 | } | |
122 | ||
123 | /** | |
124 | * Constructor. | |
125 | */ | |
126 | public GradientBar(BoundedTableModel model, int orientation) { | |
127 | super(); | |
128 | dataModel = model; | |
129 | this.orientation = orientation; | |
130 | setFocusable(false); | |
131 | updateUI(); | |
132 | } | |
133 | ||
134 | /** | |
135 | * Returns the index of the color column for the data model. | |
136 | * | |
137 | * @return index of color column | |
138 | * @see #setColorColumn(int) | |
139 | */ | |
140 | public int getColorColumn() { | |
141 | return colorColumn; | |
142 | } | |
143 | ||
144 | /** | |
145 | * Sets the index of the color column for the data model. | |
146 | * | |
147 | * @param colorColumn | |
148 | * index of color column | |
149 | * @see #getColorColumn() | |
150 | */ | |
151 | public void setColorColumn(int colorColumn) { | |
152 | int old = this.colorColumn; | |
153 | this.colorColumn = colorColumn; | |
154 | firePropertyChange(PROPERTY_COLOR_COLUMN, old, colorColumn); | |
155 | } | |
156 | ||
157 | /** | |
158 | * Returns the index of the alpha column for the alpha model. | |
159 | * | |
160 | * @return index of alpha column | |
161 | * @see #setAlphaColumn(int) | |
162 | */ | |
163 | public int getAlphaColumn() { | |
164 | return alphaColumn; | |
165 | } | |
166 | ||
167 | /** | |
168 | * Sets the index of the alpha column for the alpha model. | |
169 | * | |
170 | * @param alphaColumn | |
171 | * index of alpha column | |
172 | * @see #getAlphaColumn() | |
173 | */ | |
174 | public void setAlphaColumn(int alphaColumn) { | |
175 | int old = this.colorColumn; | |
176 | this.alphaColumn = alphaColumn; | |
177 | firePropertyChange(PROPERTY_ALPHA_COLUMN, old, alphaColumn); | |
178 | } | |
179 | ||
180 | /** | |
181 | * Regturns the current data model. The data model provides key points for | |
182 | * interpolation (position & color). | |
183 | * | |
184 | * @return current data model | |
185 | * @see #setDataModel(BoundedTableModel) | |
186 | * @see #getColorColumn() | |
187 | */ | |
188 | public BoundedTableModel getDataModel() { | |
189 | return dataModel; | |
190 | } | |
191 | ||
192 | /** | |
193 | * Sets the current data model. The data model provides key points for | |
194 | * interpolation (position & color). | |
195 | * | |
196 | * @param dataModel | |
197 | * current data model | |
198 | * @see #getDataModel() | |
199 | * @see #getColorColumn() | |
200 | */ | |
201 | public void setDataModel(BoundedTableModel dataModel) { | |
202 | BoundedTableModel old = this.dataModel; | |
203 | this.dataModel = dataModel; | |
204 | firePropertyChange(PROPERTY_DATA_MODEL, old, dataModel); | |
205 | } | |
206 | ||
207 | /** | |
208 | * Regturns the current alpha model. The data model provides alpha key | |
209 | * points for interpolation (position & alpha). | |
210 | * | |
211 | * @return current alpha model | |
212 | * @see #setAlphaModel(BoundedTableModel) | |
213 | * @see #getAlphaColumn() | |
214 | */ | |
215 | public BoundedTableModel getAlphaModel() { | |
216 | return alphaModel; | |
217 | } | |
218 | ||
219 | /** | |
220 | * Sets the current alpha model. The alpha model provides alpha key points | |
221 | * for interpolation (position & alpha). | |
222 | * | |
223 | * @param alphaModel | |
224 | * current alpha model | |
225 | * @see #getAlphaModel() | |
226 | * @see #getAlphaColumn() | |
227 | */ | |
228 | public void setAlphaModel(BoundedTableModel alphaModel) { | |
229 | BoundedTableModel old = this.alphaModel; | |
230 | this.alphaModel = alphaModel; | |
231 | firePropertyChange(PROPERTY_ALPHA_MODEL, old, alphaModel); | |
232 | } | |
233 | ||
234 | /** | |
235 | * Returns current orientation of the component. Possible values are: | |
236 | * <ul> | |
237 | * <li>SwingConstants.HORIZONTAL | |
238 | * <li>SwingConstants.VERTICAL | |
239 | * </ul> | |
240 | * | |
241 | * @return orientation | |
242 | * @see #setOrientation(int) | |
243 | */ | |
244 | public int getOrientation() { | |
245 | return orientation; | |
246 | } | |
247 | ||
248 | /** | |
249 | * Sets orientation of the component. Possible values are: | |
250 | * <ul> | |
251 | * <li>SwingConstants.HORIZONTAL | |
252 | * <li>SwingConstants.VERTICAL | |
253 | * </ul> | |
254 | * | |
255 | * @param orientation | |
256 | */ | |
257 | public void setOrientation(int orientation) { | |
258 | int old = this.orientation; | |
259 | this.orientation = orientation; | |
260 | firePropertyChange(PROPERTY_ORIENTATION, old, orientation); | |
261 | } | |
262 | ||
263 | /** | |
264 | * Returns the index of the position column for the data model. | |
265 | * | |
266 | * @return index of position column | |
267 | * @see #setColorPositionColumn(int) | |
268 | */ | |
269 | public int getColorPositionColumn() { | |
270 | return colorPositionColumn; | |
271 | } | |
272 | ||
273 | /** | |
274 | * Sets the index of the position column for the data model. | |
275 | * | |
276 | * @param positionColumn | |
277 | * index of position column | |
278 | * @see #getColorPositionColumn() | |
279 | */ | |
280 | public void setColorPositionColumn(int positionColumn) { | |
281 | int old = this.colorPositionColumn; | |
282 | this.colorPositionColumn = positionColumn; | |
283 | firePropertyChange(PROPERTY_COLOR_POSITION_COLUMN, old, positionColumn); | |
284 | } | |
285 | ||
286 | /** | |
287 | * Returns the index of the position column for the alpha model. | |
288 | * | |
289 | * @return index of position column | |
290 | * @see #setAlphaPositionColumn(int) | |
291 | */ | |
292 | public int getAlphaPositionColumn() { | |
293 | return alphaPositionColumn; | |
294 | } | |
295 | ||
296 | /** | |
297 | * Sets the index of the position column for the alpha model. | |
298 | * | |
299 | * @param positionColumn | |
300 | * index of position column | |
301 | * @see #getAlphaPositionColumn() | |
302 | */ | |
303 | public void setAlphaPositionColumn(int positionColumn) { | |
304 | int old = this.alphaPositionColumn; | |
305 | this.alphaPositionColumn = positionColumn; | |
306 | firePropertyChange(PROPERTY_ALPHA_POSITION_COLUMN, old, positionColumn); | |
307 | } | |
308 | ||
309 | } |
0 | package com.michaelbaranov.microba.gradient.ui; | |
1 | ||
2 | import java.beans.PropertyChangeEvent; | |
3 | import java.beans.PropertyChangeListener; | |
4 | ||
5 | import javax.swing.event.TableModelEvent; | |
6 | import javax.swing.event.TableModelListener; | |
7 | ||
8 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
9 | import com.michaelbaranov.microba.gradient.GradientBar; | |
10 | import com.michaelbaranov.microba.gradient.ui.basic.BasicGradientUI; | |
11 | ||
12 | public class GradientListener implements TableModelListener, | |
13 | PropertyChangeListener { | |
14 | ||
15 | private final BasicGradientUI gradientUI; | |
16 | ||
17 | private GradientBar gradient; | |
18 | ||
19 | public GradientListener(BasicGradientUI gradientUI, GradientBar gradient) { | |
20 | this.gradientUI = gradientUI; | |
21 | this.gradient = gradient; | |
22 | } | |
23 | ||
24 | public void propertyChange(PropertyChangeEvent evt) { | |
25 | if (GradientBar.PROPERTY_DATA_MODEL.equals(evt.getPropertyName())) { | |
26 | BoundedTableModel oldModel = (BoundedTableModel) evt.getOldValue(); | |
27 | BoundedTableModel newModel = (BoundedTableModel) evt.getNewValue(); | |
28 | ||
29 | if (oldModel != null) | |
30 | oldModel.removeTableModelListener(this); | |
31 | if (newModel != null) | |
32 | newModel.addTableModelListener(this); | |
33 | ||
34 | gradient.revalidate(); | |
35 | } | |
36 | ||
37 | if (GradientBar.PROPERTY_ORIENTATION.equals(evt.getPropertyName())) { | |
38 | gradient.revalidate(); | |
39 | } | |
40 | if (GradientBar.PROPERTY_COLOR_POSITION_COLUMN.equals(evt.getPropertyName())) { | |
41 | gradient.repaint(); | |
42 | } | |
43 | if (GradientBar.PROPERTY_COLOR_COLUMN.equals(evt.getPropertyName())) { | |
44 | gradient.repaint(); | |
45 | } | |
46 | if ("enabled".equals(evt.getPropertyName())) { | |
47 | gradient.repaint(); | |
48 | } | |
49 | } | |
50 | ||
51 | public void tableChanged(TableModelEvent e) { | |
52 | gradient.repaint(); | |
53 | ||
54 | } | |
55 | }⏎ |
0 | package com.michaelbaranov.microba.gradient.ui; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.awt.Insets; | |
4 | import java.awt.Rectangle; | |
5 | import java.util.Arrays; | |
6 | import java.util.Comparator; | |
7 | ||
8 | import javax.swing.plaf.ComponentUI; | |
9 | ||
10 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
11 | import com.michaelbaranov.microba.gradient.GradientBar; | |
12 | ||
13 | public class GradientUI extends ComponentUI { | |
14 | ||
15 | protected static final int PREF_BAR_HEIGHT = 32; | |
16 | ||
17 | protected static Rectangle viewRect = new Rectangle(); | |
18 | ||
19 | private PreparedColorPoint colorPoints[] = new PreparedColorPoint[0]; | |
20 | ||
21 | private PreparedAlphaPoint alphaPoints[] = new PreparedAlphaPoint[0]; | |
22 | ||
23 | protected void calculateViewRect(GradientBar gradient) { | |
24 | Insets insets = gradient.getInsets(); | |
25 | viewRect.x = insets.left; | |
26 | viewRect.y = insets.top; | |
27 | viewRect.width = gradient.getWidth() - (insets.right + viewRect.x); | |
28 | viewRect.height = gradient.getHeight() - (insets.bottom + viewRect.y); | |
29 | ||
30 | } | |
31 | ||
32 | protected PreparedColorPoint[] prepareColorPoints(BoundedTableModel model, | |
33 | int positionColumn, int colorColumn) { | |
34 | ||
35 | int numPoints = model.getRowCount(); | |
36 | ||
37 | if (numPoints > colorPoints.length) { | |
38 | colorPoints = new PreparedColorPoint[numPoints * 2]; | |
39 | for (int i = 0; i < colorPoints.length; i++) | |
40 | colorPoints[i] = new PreparedColorPoint(); | |
41 | } | |
42 | ||
43 | for (int i = 0; i < numPoints; i++) { | |
44 | PreparedColorPoint p = colorPoints[i]; | |
45 | Color c = (Color) model.getValueAt(i, colorColumn); | |
46 | p.position = ((Number) model.getValueAt(i, positionColumn)) | |
47 | .intValue(); | |
48 | p.r = c.getRed(); | |
49 | p.g = c.getGreen(); | |
50 | p.b = c.getBlue(); | |
51 | } | |
52 | ||
53 | Arrays.sort(colorPoints, 0, numPoints, PreparedColorPoint.comparator); | |
54 | ||
55 | return colorPoints; | |
56 | } | |
57 | ||
58 | protected PreparedAlphaPoint[] prepareAlphaPoints(BoundedTableModel model, | |
59 | int positionColumn, int alphaColumn) { | |
60 | ||
61 | int numPoints = model.getRowCount(); | |
62 | ||
63 | if (numPoints > alphaPoints.length) { | |
64 | alphaPoints = new PreparedAlphaPoint[numPoints * 2]; | |
65 | for (int i = 0; i < alphaPoints.length; i++) | |
66 | alphaPoints[i] = new PreparedAlphaPoint(); | |
67 | } | |
68 | ||
69 | for (int i = 0; i < numPoints; i++) { | |
70 | PreparedAlphaPoint p = alphaPoints[i]; | |
71 | float f = ((Number) model.getValueAt(i, alphaColumn)).floatValue(); | |
72 | p.position = ((Number) model.getValueAt(i, positionColumn)) | |
73 | .intValue(); | |
74 | p.alpha = f; | |
75 | } | |
76 | ||
77 | Arrays.sort(alphaPoints, 0, numPoints, PreparedAlphaPoint.comparator); | |
78 | ||
79 | return alphaPoints; | |
80 | } | |
81 | ||
82 | protected static class PreparedColorPoint { | |
83 | public static final Comparator comparator = new PtComparator(); | |
84 | ||
85 | public int r, g, b; | |
86 | ||
87 | public int position; | |
88 | ||
89 | public PreparedColorPoint() { | |
90 | } | |
91 | ||
92 | private static class PtComparator implements Comparator { | |
93 | public int compare(Object o1, Object o2) { | |
94 | PreparedColorPoint cp1 = (PreparedColorPoint) o1; | |
95 | PreparedColorPoint cp2 = (PreparedColorPoint) o2; | |
96 | if (cp1.position < cp2.position) | |
97 | return -1; | |
98 | if (cp1.position > cp2.position) | |
99 | return 1; | |
100 | return 0; | |
101 | } | |
102 | } | |
103 | } | |
104 | ||
105 | protected static class PreparedAlphaPoint { | |
106 | public static final Comparator comparator = new PtComparator(); | |
107 | ||
108 | public float alpha; | |
109 | ||
110 | public int position; | |
111 | ||
112 | public PreparedAlphaPoint() { | |
113 | } | |
114 | ||
115 | private static class PtComparator implements Comparator { | |
116 | public int compare(Object o1, Object o2) { | |
117 | PreparedColorPoint cp1 = (PreparedColorPoint) o1; | |
118 | PreparedColorPoint cp2 = (PreparedColorPoint) o2; | |
119 | if (cp1.position < cp2.position) | |
120 | return -1; | |
121 | if (cp1.position > cp2.position) | |
122 | return 1; | |
123 | return 0; | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
128 | } |
0 | package com.michaelbaranov.microba.gradient.ui.basic; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.awt.Dimension; | |
4 | import java.awt.Graphics; | |
5 | import java.awt.Graphics2D; | |
6 | import java.awt.Insets; | |
7 | import java.beans.PropertyChangeListener; | |
8 | ||
9 | import javax.swing.JComponent; | |
10 | import javax.swing.SwingConstants; | |
11 | import javax.swing.plaf.ComponentUI; | |
12 | ||
13 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
14 | import com.michaelbaranov.microba.gradient.GradientBar; | |
15 | import com.michaelbaranov.microba.gradient.ui.GradientListener; | |
16 | import com.michaelbaranov.microba.gradient.ui.GradientUI; | |
17 | ||
18 | public class BasicGradientUI extends GradientUI { | |
19 | ||
20 | public static ComponentUI createUI(JComponent c) { | |
21 | return new BasicGradientUI(); | |
22 | } | |
23 | ||
24 | public void installUI(JComponent component) { | |
25 | installListeners((GradientBar) component); | |
26 | } | |
27 | ||
28 | public void uninstallUI(JComponent component) { | |
29 | uninstallListeners((GradientBar) component); | |
30 | } | |
31 | ||
32 | protected void installListeners(GradientBar gradient) { | |
33 | GradientListener listener = createListener(gradient); | |
34 | ||
35 | if (gradient.getDataModel() != null) | |
36 | gradient.getDataModel().addTableModelListener(listener); | |
37 | gradient.addPropertyChangeListener(listener); | |
38 | } | |
39 | ||
40 | protected void uninstallListeners(GradientBar gradient) { | |
41 | GradientListener listener = lookupListsner(gradient); | |
42 | if (listener != null) { | |
43 | if (gradient.getDataModel() != null) | |
44 | gradient.getDataModel().removeTableModelListener(listener); | |
45 | gradient.removePropertyChangeListener(listener); | |
46 | } | |
47 | ||
48 | } | |
49 | ||
50 | protected GradientListener lookupListsner(GradientBar gradient) { | |
51 | PropertyChangeListener[] listeners = gradient | |
52 | .getPropertyChangeListeners(); | |
53 | ||
54 | if (listeners != null) { | |
55 | for (int counter = 0; counter < listeners.length; counter++) { | |
56 | if (listeners[counter] instanceof GradientListener) { | |
57 | return (GradientListener) listeners[counter]; | |
58 | } | |
59 | } | |
60 | } | |
61 | return null; | |
62 | } | |
63 | ||
64 | protected GradientListener createListener(GradientBar gradient) { | |
65 | return new GradientListener(this, gradient); | |
66 | } | |
67 | ||
68 | public void paint(Graphics g, JComponent c) { | |
69 | Graphics2D g2 = (Graphics2D) g; | |
70 | ||
71 | GradientBar gradient = (GradientBar) c; | |
72 | ||
73 | calculateViewRect(gradient); | |
74 | ||
75 | BoundedTableModel colorModel = gradient.getDataModel(); | |
76 | BoundedTableModel alphaModel = gradient.getAlphaModel(); | |
77 | ||
78 | if (colorModel == null) | |
79 | return; | |
80 | ||
81 | // TODO: alpha support | |
82 | ||
83 | int numColorPoints = colorModel.getRowCount(); | |
84 | int numAlphaPoints = alphaModel == null ? 0 : alphaModel.getRowCount(); | |
85 | ||
86 | int colorPositionColumn = gradient.getColorPositionColumn(); | |
87 | int colorColumn = gradient.getColorColumn(); | |
88 | ||
89 | int alphaPositionColumn = gradient.getAlphaPositionColumn(); | |
90 | int alphaColumn = gradient.getAlphaColumn(); | |
91 | ||
92 | switch (numColorPoints) { | |
93 | case 0: | |
94 | // draw checkers? | |
95 | // g.drawString("no data", viewRect.x, viewRect.y + | |
96 | // viewRect.height); | |
97 | break; | |
98 | case 1: | |
99 | g.setColor((Color) colorModel.getValueAt(0, colorColumn)); | |
100 | g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height); | |
101 | break; | |
102 | default: | |
103 | PreparedColorPoint pts[] = prepareColorPoints(colorModel, | |
104 | colorPositionColumn, colorColumn); | |
105 | // PreparedAlphaPoint apts[] = prepareAlphaPoints(alphaModel, | |
106 | // colorPositionColumn, colorColumn); | |
107 | ||
108 | int range = colorModel.getUpperBound() - colorModel.getLowerBound(); | |
109 | boolean isHorizontal = gradient.getOrientation() == SwingConstants.HORIZONTAL; | |
110 | ||
111 | for (int i = 0; i < numColorPoints - 1; i++) { | |
112 | PreparedColorPoint p0 = pts[i]; | |
113 | PreparedColorPoint p1 = pts[i + 1]; | |
114 | ||
115 | int pos0 = p0.position - colorModel.getLowerBound(); | |
116 | int pos1 = p1.position - colorModel.getLowerBound(); | |
117 | ||
118 | int pixPos0; | |
119 | int pixPos1; | |
120 | if (isHorizontal) { | |
121 | pixPos0 = viewRect.x + viewRect.width * pos0 / range; | |
122 | pixPos1 = viewRect.x + viewRect.width * pos1 / range; | |
123 | } else { | |
124 | pixPos0 = viewRect.y + viewRect.height * pos0 / range; | |
125 | pixPos1 = viewRect.y + viewRect.height * pos1 / range; | |
126 | } | |
127 | ||
128 | int pixDist = pixPos1 - pixPos0; | |
129 | ||
130 | for (int t = pixPos0; t < pixPos1; t++) { | |
131 | Color cc = interpolate(p0, p1, pixDist, t - pixPos0); | |
132 | g2.setColor(cc); | |
133 | if (isHorizontal) | |
134 | g2.drawLine(t, viewRect.y, t, viewRect.y | |
135 | + viewRect.height); | |
136 | else | |
137 | g2.drawLine(viewRect.x, t, viewRect.x + viewRect.width, | |
138 | t); | |
139 | ||
140 | } | |
141 | ||
142 | } | |
143 | break; | |
144 | } | |
145 | ||
146 | } | |
147 | ||
148 | private Color interpolate(PreparedColorPoint p0, PreparedColorPoint p1, | |
149 | int pixDist, int t) { | |
150 | int r = p0.r + (p1.r - p0.r) * t / pixDist; | |
151 | int g = p0.g + (p1.g - p0.g) * t / pixDist; | |
152 | int b = p0.b + (p1.b - p0.b) * t / pixDist; | |
153 | return new Color(r, g, b); | |
154 | } | |
155 | ||
156 | public Dimension getMinimumSize(JComponent c) { | |
157 | return new Dimension(1, 1); | |
158 | } | |
159 | ||
160 | public Dimension getPreferredSize(JComponent c) { | |
161 | ||
162 | GradientBar gradient = (GradientBar) c; | |
163 | BoundedTableModel dataModel = gradient.getDataModel(); | |
164 | Insets ins = gradient.getInsets(); | |
165 | ||
166 | int r; | |
167 | if (dataModel == null) | |
168 | r = 1; | |
169 | else | |
170 | r = dataModel.getUpperBound() - dataModel.getLowerBound(); | |
171 | ||
172 | if (gradient.getOrientation() == SwingConstants.HORIZONTAL) | |
173 | return new Dimension((r) * 2 + ins.left + ins.right + 1, | |
174 | PREF_BAR_HEIGHT + ins.top + ins.bottom + 1); | |
175 | else | |
176 | return new Dimension(PREF_BAR_HEIGHT + ins.top + ins.bottom + 1, | |
177 | (r) * 2 + ins.left + ins.right + 1); | |
178 | ||
179 | } | |
180 | ||
181 | } |
+127
-0
0 | package com.michaelbaranov.microba.gradienteditor; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.util.ArrayList; | |
4 | import java.util.List; | |
5 | ||
6 | import javax.swing.ListSelectionModel; | |
7 | ||
8 | import com.michaelbaranov.microba.common.AbstractBoundedTableModelWithSelection; | |
9 | import com.michaelbaranov.microba.marker.MarkerMutationModel; | |
10 | ||
11 | /** | |
12 | * A basic implementation of {@link AbstractBoundedTableModelWithSelection} and | |
13 | * {@link MarkerMutationModel}. Used by default by {@link GradientEditor} as a | |
14 | * color model, color selection model and color mutation model. | |
15 | * | |
16 | * <p> | |
17 | * This implementation is mutable. | |
18 | * | |
19 | * @author Michael Baranov | |
20 | * | |
21 | */ | |
22 | public class DefaultGradientEditorModel extends | |
23 | AbstractBoundedTableModelWithSelection implements MarkerMutationModel { | |
24 | ||
25 | public static final int POSITION_COLUMN = 0; | |
26 | ||
27 | public static final int COLOR_COLUMN = 1; | |
28 | ||
29 | protected List position = new ArrayList(32); | |
30 | ||
31 | protected List color = new ArrayList(32); | |
32 | ||
33 | public DefaultGradientEditorModel() { | |
34 | super(); | |
35 | setSelectionMode(ListSelectionModel.SINGLE_SELECTION); | |
36 | ||
37 | position.add(new Integer(0)); | |
38 | color.add(Color.BLACK); | |
39 | ||
40 | position.add(new Integer(255)); | |
41 | color.add(Color.WHITE); | |
42 | ||
43 | } | |
44 | ||
45 | public void removeMarkerAtIndex(int index) { | |
46 | if (isSelectedIndex(index)) { | |
47 | removeSelectionInterval(index, index); | |
48 | } | |
49 | position.remove(index); | |
50 | color.remove(index); | |
51 | fireTableRowsDeleted(index, index); | |
52 | ||
53 | } | |
54 | ||
55 | public int addMarkAtPosition(int pos) { | |
56 | position.add(new Integer(pos)); | |
57 | color.add(new Color((float) Math.random(), (float) Math.random(), | |
58 | (float) Math.random())); | |
59 | int index = position.size() - 1; | |
60 | fireTableRowsInserted(index, index); | |
61 | return index; | |
62 | } | |
63 | ||
64 | public int getLowerBound() { | |
65 | return 0; | |
66 | } | |
67 | ||
68 | public int getUpperBound() { | |
69 | return 255; | |
70 | } | |
71 | ||
72 | public int getRowCount() { | |
73 | return position.size(); | |
74 | } | |
75 | ||
76 | public int getColumnCount() { | |
77 | return 2; | |
78 | } | |
79 | ||
80 | public Class getColumnClass(int columnIndex) { | |
81 | switch (columnIndex) { | |
82 | case POSITION_COLUMN: | |
83 | return Integer.class; | |
84 | case COLOR_COLUMN: | |
85 | return Color.class; | |
86 | } | |
87 | return super.getColumnClass(columnIndex); | |
88 | } | |
89 | ||
90 | public Object getValueAt(int rowIndex, int columnIndex) { | |
91 | switch (columnIndex) { | |
92 | case POSITION_COLUMN: | |
93 | return position.get(rowIndex); | |
94 | case COLOR_COLUMN: | |
95 | return color.get(rowIndex); | |
96 | } | |
97 | return null; | |
98 | } | |
99 | ||
100 | public void setValueAt(Object aValue, int rowIndex, int columnIndex) { | |
101 | switch (columnIndex) { | |
102 | case POSITION_COLUMN: | |
103 | ||
104 | for (int i = 0; i < position.size(); i++) | |
105 | if (rowIndex != i && aValue.equals(position.get(i))) { | |
106 | return; | |
107 | } | |
108 | ||
109 | position.remove(rowIndex); | |
110 | position.add(rowIndex, aValue); | |
111 | fireTableCellUpdated(rowIndex, columnIndex); | |
112 | break; | |
113 | ||
114 | case COLOR_COLUMN: | |
115 | color.remove(rowIndex); | |
116 | color.add(rowIndex, (Color) aValue); | |
117 | fireTableCellUpdated(rowIndex, columnIndex); | |
118 | break; | |
119 | } | |
120 | } | |
121 | ||
122 | public boolean isCellEditable(int rowIndex, int columnIndex) { | |
123 | // protecting first 2 rows (first and last marker) from being moved | |
124 | return !(columnIndex == POSITION_COLUMN && rowIndex < 2); | |
125 | } | |
126 | } |
0 | package com.michaelbaranov.microba.gradienteditor; | |
1 | ||
2 | import javax.swing.JColorChooser; | |
3 | import javax.swing.ListSelectionModel; | |
4 | ||
5 | import com.michaelbaranov.microba.gradient.GradientBar; | |
6 | import com.michaelbaranov.microba.marker.MarkerBar; | |
7 | import com.michaelbaranov.microba.marker.MarkerMutationModel; | |
8 | ||
9 | /** | |
10 | * IMPORTANT: alpha featre not implemented. Stubs only. No alpha marker bar yet. | |
11 | * <p> | |
12 | * | |
13 | * This is a component for displaying/modifying a gradient (palette). | |
14 | * | |
15 | * <p> | |
16 | * Implementation details: <br> | |
17 | * This implementation combines a {@link GradientBar} with two {@link MarkerBar} | |
18 | * components. The marker bars are used to provide editing capabilities to the | |
19 | * gradient bar. Note, that this component doesn't provide direct | |
20 | * color-selecting capabilitied but relies on other external components such as | |
21 | * {@link JColorChooser}. | |
22 | * | |
23 | * @author Michael Baranov | |
24 | * | |
25 | */ | |
26 | public class GradientEditor extends GradientBar { | |
27 | ||
28 | /** | |
29 | * The name of a "colorSelectionModel" property. | |
30 | */ | |
31 | public static final String PROPERTY_COLOR_SELECTION_MODEL = "colorSelectionModel"; | |
32 | ||
33 | /** | |
34 | * The name of a "alphaSelectionModel" property. | |
35 | */ | |
36 | public static final String PROPERTY_ALPHA_SELECTION_MODEL = "alphaSelectionModel"; | |
37 | ||
38 | /** | |
39 | * The name of a "colorMutationModel" property. | |
40 | */ | |
41 | public static final String PROPERTY_COLOR_MUTATION_MODEL = "colorMutationModel"; | |
42 | ||
43 | /** | |
44 | * The name of a "alphaMutationModel" property. | |
45 | */ | |
46 | public static final String PROPERTY_ALPHA_MUTATION_MODEL = "alphaMutationModel"; | |
47 | ||
48 | private static final String uiClassID = "microba.GradientEditorUI"; | |
49 | ||
50 | private ListSelectionModel colorSelectionModel = null; | |
51 | ||
52 | private ListSelectionModel alphaSelectionModel = null; | |
53 | ||
54 | private MarkerMutationModel colorMutationModel = null; | |
55 | ||
56 | private MarkerMutationModel alphaMutationModel = null; | |
57 | ||
58 | /** | |
59 | * Constructor. | |
60 | */ | |
61 | public GradientEditor() { | |
62 | super(); | |
63 | DefaultGradientEditorModel defaultGradientEditorModel = new DefaultGradientEditorModel(); | |
64 | dataModel = defaultGradientEditorModel; | |
65 | colorSelectionModel = defaultGradientEditorModel; | |
66 | colorMutationModel = defaultGradientEditorModel; | |
67 | setFocusable(true); | |
68 | updateUI(); | |
69 | } | |
70 | ||
71 | public String getUIClassID() { | |
72 | return uiClassID; | |
73 | } | |
74 | ||
75 | /** | |
76 | * Regturns the current color mutation model. | |
77 | * | |
78 | * @return current color mutation model | |
79 | * @see #setColorMutationModel(MarkerMutationModel) | |
80 | * @see MarkerMutationModel | |
81 | */ | |
82 | public MarkerMutationModel getColorMutationModel() { | |
83 | return colorMutationModel; | |
84 | } | |
85 | ||
86 | /** | |
87 | * Replaces current color mutation model with given one. | |
88 | * | |
89 | * @param mutationModel | |
90 | * new mutation model. May be <code>null<code>. | |
91 | * @see #getColorMutationModel() | |
92 | * @see MarkerMutationModel | |
93 | */ | |
94 | public void setColorMutationModel(MarkerMutationModel mutationModel) { | |
95 | MarkerMutationModel oldMutationModel = this.colorMutationModel; | |
96 | this.colorMutationModel = mutationModel; | |
97 | firePropertyChange(PROPERTY_COLOR_MUTATION_MODEL, oldMutationModel, mutationModel); | |
98 | } | |
99 | ||
100 | /** | |
101 | * Returns current color selection model. | |
102 | * | |
103 | * @return current color selection model. | |
104 | * @see #setColorSelectionModel(ListSelectionModel) | |
105 | */ | |
106 | public ListSelectionModel getColorSelectionModel() { | |
107 | return colorSelectionModel; | |
108 | } | |
109 | ||
110 | /** | |
111 | * Replaces current color selection model with given one. This | |
112 | * implementation uses | |
113 | * <code>{@link ListSelectionModel#getLeadSelectionIndex()}</code> to | |
114 | * determine selected marker. | |
115 | * | |
116 | * @param selectionModel | |
117 | * new selection model. May be <code>null<code>. | |
118 | * | |
119 | * @see #getColorSelectionModel() | |
120 | */ | |
121 | public void setColorSelectionModel(ListSelectionModel selectionModel) { | |
122 | ListSelectionModel oldSelectionModel = this.colorSelectionModel; | |
123 | this.colorSelectionModel = selectionModel; | |
124 | firePropertyChange(PROPERTY_COLOR_SELECTION_MODEL, oldSelectionModel, | |
125 | selectionModel); | |
126 | } | |
127 | ||
128 | /** | |
129 | * Returns current alpha selection model. | |
130 | * | |
131 | * @return current alpha selection model. | |
132 | * @see #setAlphaSelectionModel(ListSelectionModel) | |
133 | */ | |
134 | public ListSelectionModel getAlphaSelectionModel() { | |
135 | return alphaSelectionModel; | |
136 | } | |
137 | ||
138 | /** | |
139 | * Replaces current alpha selection model with given one. This | |
140 | * implementation uses | |
141 | * <code>{@link ListSelectionModel#getLeadSelectionIndex()}</code> to | |
142 | * determine selected marker. | |
143 | * | |
144 | * @param selectionModel | |
145 | * new selection model. May be <code>null<code>. | |
146 | * | |
147 | * @see #getAlphaSelectionModel() | |
148 | */ | |
149 | public void setAlphaSelectionModel(ListSelectionModel selectionModel) { | |
150 | this.alphaSelectionModel = selectionModel; | |
151 | } | |
152 | ||
153 | /** | |
154 | * Regturns the current alpha mutation model. | |
155 | * | |
156 | * @return current alpha mutation model | |
157 | * @see #setAlphaMutationModel(MarkerMutationModel) | |
158 | * @see MarkerMutationModel | |
159 | */ | |
160 | public MarkerMutationModel getAlphaMutationModel() { | |
161 | return alphaMutationModel; | |
162 | } | |
163 | ||
164 | /** | |
165 | * Replaces current alpha mutation model with given one. | |
166 | * | |
167 | * @param mutationModel | |
168 | * new mutation model. May be <code>null<code>. | |
169 | * @see #getAlphaMutationModel() | |
170 | * @see MarkerMutationModel | |
171 | */ | |
172 | public void setAlphaMutationModel(MarkerMutationModel mutationModel) { | |
173 | this.alphaMutationModel = mutationModel; | |
174 | } | |
175 | ||
176 | } |
+60
-0
0 | package com.michaelbaranov.microba.gradienteditor.ui; | |
1 | ||
2 | import java.awt.Component; | |
3 | import java.awt.Container; | |
4 | import java.awt.Dimension; | |
5 | import java.awt.Insets; | |
6 | import java.awt.LayoutManager; | |
7 | ||
8 | import javax.swing.SwingConstants; | |
9 | ||
10 | import com.michaelbaranov.microba.gradient.GradientBar; | |
11 | import com.michaelbaranov.microba.gradienteditor.GradientEditor; | |
12 | import com.michaelbaranov.microba.marker.MarkerBar; | |
13 | ||
14 | public class GradientEditorLayout implements LayoutManager { | |
15 | ||
16 | private MarkerBar bar; | |
17 | ||
18 | private GradientBar gradient; | |
19 | ||
20 | public GradientEditorLayout(MarkerBar bar, GradientBar gradient) { | |
21 | this.bar = bar; | |
22 | this.gradient = gradient; | |
23 | } | |
24 | ||
25 | public void addLayoutComponent(String name, Component comp) { | |
26 | } | |
27 | ||
28 | public void removeLayoutComponent(Component comp) { | |
29 | } | |
30 | ||
31 | public Dimension preferredLayoutSize(Container parent) { | |
32 | return parent.getPreferredSize(); | |
33 | } | |
34 | ||
35 | public Dimension minimumLayoutSize(Container parent) { | |
36 | return parent.getMinimumSize(); | |
37 | } | |
38 | ||
39 | public void layoutContainer(Container parent) { | |
40 | GradientEditor e = (GradientEditor) parent; | |
41 | Insets i = parent.getInsets(); | |
42 | int gap = bar.getMarkerSideGap(); | |
43 | if (e.getOrientation() == SwingConstants.HORIZONTAL) { | |
44 | bar.setBounds(i.left, i.top, parent.getWidth() - i.left - i.right, | |
45 | bar.getPreferredSize().height); | |
46 | gradient.setBounds(i.left + gap, bar.getBounds().y | |
47 | + bar.getBounds().height, parent.getWidth() - i.left | |
48 | - i.right - gap - gap, parent.getHeight() - i.top | |
49 | - i.bottom - bar.getHeight()); | |
50 | } else { | |
51 | gradient.setBounds(i.left, i.top+gap, | |
52 | gradient.getPreferredSize().width,parent.getHeight() - i.top - i.bottom-gap-gap); | |
53 | bar.setBounds(gradient.getBounds().x+gradient.getBounds().width ,i.top, | |
54 | bar.getPreferredSize().width,gradient.getBounds().height+gap+gap); | |
55 | ||
56 | } | |
57 | } | |
58 | ||
59 | } |
+27
-0
0 | package com.michaelbaranov.microba.gradienteditor.ui; | |
1 | ||
2 | import java.beans.PropertyChangeEvent; | |
3 | import java.beans.PropertyChangeListener; | |
4 | ||
5 | import com.michaelbaranov.microba.gradienteditor.GradientEditor; | |
6 | ||
7 | public class GradientEditorListener implements PropertyChangeListener { | |
8 | ||
9 | private GradientEditorUI ui; | |
10 | ||
11 | private GradientEditor editor; | |
12 | ||
13 | public GradientEditorListener(GradientEditor editor, GradientEditorUI ui) { | |
14 | super(); | |
15 | this.editor = editor; | |
16 | this.ui = ui; | |
17 | } | |
18 | ||
19 | public void propertyChange(PropertyChangeEvent evt) { | |
20 | if (GradientEditor.PROPERTY_DATA_MODEL.equals(evt.getPropertyName())) { | |
21 | ||
22 | } | |
23 | ||
24 | } | |
25 | ||
26 | } |
0 | package com.michaelbaranov.microba.gradienteditor.ui; | |
1 | ||
2 | import javax.swing.plaf.ComponentUI; | |
3 | ||
4 | public class GradientEditorUI extends ComponentUI{ | |
5 | ||
6 | } |
+172
-0
0 | package com.michaelbaranov.microba.gradienteditor.ui.basic; | |
1 | ||
2 | import java.awt.Dimension; | |
3 | import java.awt.FlowLayout; | |
4 | import java.beans.PropertyChangeEvent; | |
5 | import java.beans.PropertyChangeListener; | |
6 | ||
7 | import javax.swing.JComponent; | |
8 | import javax.swing.ListSelectionModel; | |
9 | import javax.swing.SwingConstants; | |
10 | import javax.swing.plaf.ComponentUI; | |
11 | ||
12 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
13 | import com.michaelbaranov.microba.gradient.GradientBar; | |
14 | import com.michaelbaranov.microba.gradienteditor.GradientEditor; | |
15 | import com.michaelbaranov.microba.gradienteditor.ui.GradientEditorLayout; | |
16 | import com.michaelbaranov.microba.gradienteditor.ui.GradientEditorUI; | |
17 | import com.michaelbaranov.microba.marker.MarkerBar; | |
18 | import com.michaelbaranov.microba.marker.MarkerMutationModel; | |
19 | ||
20 | public class BasicGradientEditorUI extends GradientEditorUI { | |
21 | ||
22 | protected GradientBar gradientBar; | |
23 | ||
24 | protected MarkerBar colorBar; | |
25 | ||
26 | protected MarkerBar alphaBar; | |
27 | ||
28 | private GradientEditor gradient; | |
29 | ||
30 | private GradientEditorListener editorListener; | |
31 | ||
32 | public static ComponentUI createUI(JComponent c) { | |
33 | return new BasicGradientEditorUI(); | |
34 | } | |
35 | ||
36 | public void installUI(JComponent component) { | |
37 | gradient = (GradientEditor) component; | |
38 | editorListener = new GradientEditorListener(); | |
39 | createAndConfigureSubcomponents(); | |
40 | installSubcomponents(); | |
41 | installListeners(); | |
42 | gradient.revalidate(); | |
43 | } | |
44 | ||
45 | public void uninstallUI(JComponent component) { | |
46 | // JGradient gradient = (JGradient) component; | |
47 | uninstallSubcomponents(); | |
48 | uninstallListeners(); | |
49 | } | |
50 | ||
51 | protected void installSubcomponents() { | |
52 | gradient.setLayout(new GradientEditorLayout(colorBar, gradientBar)); | |
53 | gradient.add(colorBar); | |
54 | gradient.add(gradientBar); | |
55 | } | |
56 | ||
57 | protected void uninstallSubcomponents() { | |
58 | gradient.setLayout(new FlowLayout()); | |
59 | gradient.remove(colorBar); | |
60 | gradient.remove(gradientBar); | |
61 | } | |
62 | ||
63 | private void installListeners() { | |
64 | gradient.addPropertyChangeListener(editorListener); | |
65 | } | |
66 | ||
67 | private void uninstallListeners() { | |
68 | gradient.removePropertyChangeListener(editorListener); | |
69 | } | |
70 | ||
71 | private void createAndConfigureSubcomponents() { | |
72 | gradientBar = new GradientBar(gradient.getDataModel()); | |
73 | gradientBar.setOrientation(gradient.getOrientation()); | |
74 | gradientBar.setColorPositionColumn(gradient.getColorPositionColumn()); | |
75 | gradientBar.setColorColumn(gradient.getColorColumn()); | |
76 | ||
77 | colorBar = new MarkerBar(gradient.getDataModel(), gradient | |
78 | .getColorSelectionModel()); | |
79 | colorBar.setOrientation(gradient.getOrientation()); | |
80 | colorBar.setMutationModel(gradient.getColorMutationModel()); | |
81 | colorBar.setPositionColumn(gradient.getColorPositionColumn()); | |
82 | colorBar.setColorColumn(gradient.getColorColumn()); | |
83 | ||
84 | alphaBar = new MarkerBar(gradient.getDataModel(), gradient | |
85 | .getColorSelectionModel()); | |
86 | alphaBar.setOrientation(gradient.getOrientation()); | |
87 | alphaBar.setMutationModel(gradient.getColorMutationModel()); | |
88 | alphaBar.setPositionColumn(gradient.getColorPositionColumn()); | |
89 | alphaBar.setColorColumn(gradient.getColorColumn()); | |
90 | } | |
91 | ||
92 | class GradientEditorListener implements PropertyChangeListener { | |
93 | ||
94 | public void propertyChange(PropertyChangeEvent evt) { | |
95 | if (GradientEditor.PROPERTY_DATA_MODEL | |
96 | .equals(evt.getPropertyName())) { | |
97 | gradientBar.setDataModel((BoundedTableModel) evt.getNewValue()); | |
98 | colorBar.setDataModel((BoundedTableModel) evt.getNewValue()); | |
99 | } | |
100 | if (GradientEditor.PROPERTY_COLOR_SELECTION_MODEL.equals(evt | |
101 | .getPropertyName())) { | |
102 | colorBar.setSelectionModel((ListSelectionModel) evt | |
103 | .getNewValue()); | |
104 | } | |
105 | if (GradientEditor.PROPERTY_COLOR_MUTATION_MODEL.equals(evt | |
106 | .getPropertyName())) { | |
107 | colorBar.setMutationModel((MarkerMutationModel) evt | |
108 | .getNewValue()); | |
109 | } | |
110 | if (GradientEditor.PROPERTY_COLOR_POSITION_COLUMN.equals(evt | |
111 | .getPropertyName())) { | |
112 | colorBar.setPositionColumn(((Integer) evt.getNewValue()) | |
113 | .intValue()); | |
114 | gradientBar | |
115 | .setColorPositionColumn(((Integer) evt.getNewValue()) | |
116 | .intValue()); | |
117 | } | |
118 | if (GradientEditor.PROPERTY_COLOR_COLUMN.equals(evt | |
119 | .getPropertyName())) { | |
120 | gradientBar.setColorColumn(((Integer) evt.getNewValue()) | |
121 | .intValue()); | |
122 | colorBar.setColorColumn(((Integer) evt.getNewValue()) | |
123 | .intValue()); | |
124 | ||
125 | } | |
126 | if (GradientEditor.PROPERTY_ORIENTATION.equals(evt | |
127 | .getPropertyName())) { | |
128 | colorBar.setOrientation(((Integer) evt.getNewValue()) | |
129 | .intValue()); | |
130 | gradientBar.setOrientation(((Integer) evt.getNewValue()) | |
131 | .intValue()); | |
132 | } | |
133 | if ("enabled".equals(evt.getPropertyName())) { | |
134 | colorBar.setEnabled(((Boolean) evt.getNewValue()) | |
135 | .booleanValue()); | |
136 | gradientBar.setEnabled(((Boolean) evt.getNewValue()) | |
137 | .booleanValue()); | |
138 | } | |
139 | ||
140 | } | |
141 | ||
142 | } | |
143 | ||
144 | public Dimension getMinimumSize(JComponent c) { | |
145 | GradientBar gradient = (GradientBar) c; | |
146 | ||
147 | Dimension minimumSize = gradientBar.getMinimumSize(); | |
148 | Dimension minimumSize2 = colorBar.getMinimumSize(); | |
149 | if (gradient.getOrientation() == SwingConstants.HORIZONTAL) | |
150 | return new Dimension(Math | |
151 | .max(minimumSize.width, minimumSize2.width), | |
152 | minimumSize.height + minimumSize2.height); | |
153 | else | |
154 | return new Dimension(minimumSize.width + minimumSize2.width, Math | |
155 | .max(minimumSize.height, minimumSize2.height)); | |
156 | ||
157 | } | |
158 | ||
159 | public Dimension getPreferredSize(JComponent c) { | |
160 | Dimension preferredSize = gradientBar.getPreferredSize(); | |
161 | Dimension preferredSize2 = colorBar.getPreferredSize(); | |
162 | if (gradient.getOrientation() == SwingConstants.HORIZONTAL) | |
163 | return new Dimension(Math.max(preferredSize.width, | |
164 | preferredSize2.width), preferredSize.height | |
165 | + preferredSize2.height); | |
166 | else | |
167 | return new Dimension(preferredSize.width + preferredSize2.width, | |
168 | Math.max(preferredSize.height, preferredSize2.height)); | |
169 | } | |
170 | ||
171 | } |
0 | package com.michaelbaranov.microba.jgraph.birdview; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.awt.Dimension; | |
4 | import java.awt.Graphics; | |
5 | import java.awt.Graphics2D; | |
6 | import java.awt.Point; | |
7 | import java.awt.Rectangle; | |
8 | import java.awt.event.AdjustmentEvent; | |
9 | import java.awt.event.AdjustmentListener; | |
10 | import java.awt.event.ComponentEvent; | |
11 | import java.awt.event.ComponentListener; | |
12 | import java.awt.event.MouseEvent; | |
13 | import java.awt.event.MouseListener; | |
14 | import java.awt.event.MouseMotionListener; | |
15 | import java.awt.geom.Point2D; | |
16 | import java.awt.geom.Rectangle2D; | |
17 | import java.beans.PropertyChangeEvent; | |
18 | import java.beans.PropertyChangeListener; | |
19 | ||
20 | import javax.swing.JPanel; | |
21 | import javax.swing.JScrollPane; | |
22 | import javax.swing.UIManager; | |
23 | ||
24 | import org.jgraph.JGraph; | |
25 | import org.jgraph.event.GraphModelEvent; | |
26 | import org.jgraph.event.GraphModelListener; | |
27 | import org.jgraph.graph.GraphLayoutCache; | |
28 | ||
29 | /** | |
30 | * A JFC/Swing component that displays a bird-eyes (thumbnail) view of a | |
31 | * {@link JGraph} in combination with {@link JScrollPane}. Also allows to pan | |
32 | * view with the mouse. | |
33 | * | |
34 | * | |
35 | * @author Michael Baranov | |
36 | * | |
37 | */ | |
38 | public class Birdview extends JPanel { | |
39 | ||
40 | /** | |
41 | * Color constant | |
42 | */ | |
43 | public static final Color PAN_RECT_COLOR = Color.black; | |
44 | ||
45 | /** | |
46 | * Color constant | |
47 | */ | |
48 | public static final Color BACKGROUND_COLOR = UIManager.getColor("Panel.background"); | |
49 | ||
50 | /** | |
51 | * a {@link JGraph} component used as main display | |
52 | */ | |
53 | private JGraph displayGraph; | |
54 | ||
55 | /** | |
56 | * a {@link JScrollPane} to track | |
57 | */ | |
58 | private JScrollPane peerScroller; | |
59 | ||
60 | /** | |
61 | * a {@link JGraph} to tracl | |
62 | */ | |
63 | private JGraph peerGraph; | |
64 | ||
65 | /** | |
66 | * a rect, subrect of the component area, that matches the tracked view | |
67 | */ | |
68 | private Rectangle2D paintRect; | |
69 | ||
70 | /** | |
71 | * a rect, a subrect of the component area, that matches the tracked | |
72 | * viewport | |
73 | */ | |
74 | private Rectangle2D panRect; | |
75 | ||
76 | /** | |
77 | * the scale applied to {@link #displayGraph} to fit into {@link #paintRect} | |
78 | */ | |
79 | private double scale = 1.0; | |
80 | ||
81 | /** | |
82 | * A listener | |
83 | */ | |
84 | private ScrollerListener scrollerListener = new ScrollerListener(); | |
85 | ||
86 | /** | |
87 | * A listener | |
88 | */ | |
89 | private SelfMouseListener selfMouseListener = new SelfMouseListener(); | |
90 | ||
91 | /** | |
92 | * A listener | |
93 | */ | |
94 | private GraphPropertyChangeListener graphPropertyChangeListener = new GraphPropertyChangeListener(); | |
95 | ||
96 | /** | |
97 | * Constructor. | |
98 | * | |
99 | * @param doc | |
100 | * document to track. May be <code>null</code> | |
101 | */ | |
102 | public Birdview() { | |
103 | ||
104 | displayGraph = new JGraph(); | |
105 | displayGraph.setEnabled(false); | |
106 | displayGraph.setAntiAliased(true); | |
107 | displayGraph.addMouseListener(selfMouseListener); | |
108 | displayGraph.addMouseMotionListener(selfMouseListener); | |
109 | ||
110 | this.setLayout(null); | |
111 | this.addComponentListener(new SelfResizeListener()); | |
112 | this.add(displayGraph); | |
113 | ||
114 | } | |
115 | ||
116 | /** | |
117 | * Makes this component track the provided graph and scroller. Set | |
118 | * parameters to <code>null</code> to unbind thie component. | |
119 | * | |
120 | * @param graph | |
121 | * the graph component. May be <code>null</code> | |
122 | * @param scroller | |
123 | * the croller, usually the one that holds the graph. May be | |
124 | * <code>null</code> | |
125 | */ | |
126 | public void setTrackingFor(JGraph graph, JScrollPane scroller) { | |
127 | if (this.peerGraph != null) { | |
128 | this.peerGraph.getModel().removeGraphModelListener(scrollerListener); | |
129 | this.peerGraph.removePropertyChangeListener(graphPropertyChangeListener); | |
130 | } | |
131 | ||
132 | this.peerGraph = graph; | |
133 | ||
134 | if (this.peerGraph != null) { | |
135 | this.peerGraph.getModel().addGraphModelListener(scrollerListener); | |
136 | this.peerGraph.addPropertyChangeListener(graphPropertyChangeListener); | |
137 | ||
138 | this.displayGraph.setGraphLayoutCache(peerGraph.getGraphLayoutCache()); | |
139 | ||
140 | } else { | |
141 | this.displayGraph.setGraphLayoutCache(new GraphLayoutCache()); | |
142 | } | |
143 | ||
144 | // | |
145 | if (this.peerScroller != null) { | |
146 | this.peerScroller.getHorizontalScrollBar().removeAdjustmentListener( | |
147 | scrollerListener); | |
148 | this.peerScroller.getVerticalScrollBar().removeAdjustmentListener( | |
149 | scrollerListener); | |
150 | } | |
151 | ||
152 | this.peerScroller = scroller; | |
153 | ||
154 | if (this.peerScroller != null) { | |
155 | this.peerScroller.getHorizontalScrollBar().addAdjustmentListener( | |
156 | scrollerListener); | |
157 | this.peerScroller.getVerticalScrollBar().addAdjustmentListener( | |
158 | scrollerListener); | |
159 | } | |
160 | ||
161 | update(); | |
162 | repaint(); | |
163 | ||
164 | } | |
165 | ||
166 | /** | |
167 | * Calculates {@link #paintRect}, {@link #panRect}, {@link #scale} based | |
168 | * on observations of peerScroller. | |
169 | * | |
170 | */ | |
171 | private void update() { | |
172 | if (peerScroller != null) { | |
173 | Dimension viewSize = peerScroller.getViewport().getViewSize(); | |
174 | double viewAspect = viewSize.getHeight() / viewSize.getWidth(); | |
175 | ||
176 | Dimension panelSize = this.getSize(); | |
177 | double panelAspect = panelSize.getHeight() / panelSize.getWidth(); | |
178 | ||
179 | if (panelAspect < viewAspect) { | |
180 | // panel is wider, height is primary | |
181 | double desiredPanelWidth = panelSize.getHeight() / viewAspect; | |
182 | double blankWidth = panelSize.getWidth() - desiredPanelWidth; | |
183 | double gap = blankWidth / 2; | |
184 | paintRect = new Rectangle2D.Double(gap, 0, desiredPanelWidth, | |
185 | panelSize.height); | |
186 | scale = panelSize.getHeight() / viewSize.getHeight(); | |
187 | scale *= peerGraph.getScale(); | |
188 | } else { | |
189 | // panel is heigher, width is primary | |
190 | double desiredPanelHeight = panelSize.getWidth() * viewAspect; | |
191 | double blankHeight = panelSize.getHeight() - desiredPanelHeight; | |
192 | double gap = blankHeight / 2; | |
193 | paintRect = new Rectangle2D.Double(0, gap, panelSize.getWidth(), | |
194 | desiredPanelHeight); | |
195 | scale = panelSize.getWidth() / viewSize.getWidth(); | |
196 | scale *= peerGraph.getScale(); | |
197 | } | |
198 | ||
199 | Rectangle viewRect = peerScroller.getViewport().getViewRect(); | |
200 | double shiftX = viewRect.getX() / viewSize.getWidth(); | |
201 | double shiftY = viewRect.getY() / viewSize.getHeight(); | |
202 | ||
203 | double sizeX = viewRect.getWidth() / viewSize.getWidth(); | |
204 | double sizeY = viewRect.getHeight() / viewSize.getHeight(); | |
205 | ||
206 | panRect = new Rectangle2D.Double(paintRect.getX() + paintRect.getWidth() | |
207 | * shiftX, paintRect.getY() + paintRect.getHeight() * shiftY, | |
208 | paintRect.getWidth() * sizeX, paintRect.getHeight() * sizeY); | |
209 | ||
210 | } else { | |
211 | panRect = null; | |
212 | paintRect = null; | |
213 | scale = 1; | |
214 | } | |
215 | } | |
216 | ||
217 | /** | |
218 | * Addjusts {@link #panRect} to be postitionsed with the centter at a given | |
219 | * point if possible. This methods ensures that {@link #panRect} fits | |
220 | * entirely in {@link #paintRect}. | |
221 | * | |
222 | * @param point | |
223 | * the desired panRect center | |
224 | */ | |
225 | private void panRectTo(Point point) { | |
226 | ||
227 | Dimension viewSize = peerScroller.getViewport().getViewSize(); | |
228 | Rectangle viewRect = peerScroller.getViewport().getViewRect(); | |
229 | ||
230 | double panHalfWidth = panRect.getWidth() / 2; | |
231 | double panHalfHeight = panRect.getHeight() / 2; | |
232 | ||
233 | Point2D panOrigin = new Point2D.Double(point.x - panHalfWidth, point.y | |
234 | - panHalfHeight); | |
235 | double xk = panOrigin.getX() / paintRect.getWidth(); | |
236 | double yk = panOrigin.getY() / paintRect.getHeight(); | |
237 | ||
238 | Point viewPos = new Point((int) (viewSize.getWidth() * xk), (int) (viewSize | |
239 | .getHeight() * yk)); | |
240 | ||
241 | // make sure we do not pan past the bounds: | |
242 | if (viewPos.x < 0) | |
243 | viewPos.x = 0; | |
244 | if (viewPos.y < 0) | |
245 | viewPos.y = 0; | |
246 | ||
247 | int wd = (viewPos.x + viewRect.width) - viewSize.width; | |
248 | int hd = (viewPos.y + viewRect.height) - viewSize.height; | |
249 | ||
250 | if (wd > 0) | |
251 | viewPos.x -= wd; | |
252 | if (hd > 0) | |
253 | viewPos.y -= hd; | |
254 | ||
255 | // pan it | |
256 | peerScroller.getViewport().setViewPosition(viewPos); | |
257 | ||
258 | update(); | |
259 | repaint(); | |
260 | } | |
261 | ||
262 | public void paint(Graphics g) { | |
263 | Graphics2D g2 = (Graphics2D) g; | |
264 | ||
265 | // fill background | |
266 | g2.setColor(BACKGROUND_COLOR); | |
267 | g2.fillRect(0, 0, this.getWidth(), this.getHeight()); | |
268 | ||
269 | if (peerGraph != null) { | |
270 | // paint the graph display | |
271 | displayGraph.setBounds(paintRect.getBounds()); | |
272 | displayGraph.setScale(scale); | |
273 | displayGraph.setBackground(peerGraph.getBackground()); | |
274 | paintChildren(g); | |
275 | } | |
276 | ||
277 | if (panRect != null) { | |
278 | // draw pan rect | |
279 | g2.setColor(PAN_RECT_COLOR); | |
280 | g2.draw(panRect.getBounds()); | |
281 | } | |
282 | } | |
283 | ||
284 | /** | |
285 | * A listener to watch for scrollbar movement. | |
286 | * | |
287 | * @author Michael Baranov | |
288 | * | |
289 | */ | |
290 | private class ScrollerListener implements AdjustmentListener, GraphModelListener { | |
291 | ||
292 | public void adjustmentValueChanged(AdjustmentEvent e) { | |
293 | update(); | |
294 | repaint(); | |
295 | } | |
296 | ||
297 | public void graphChanged(GraphModelEvent e) { | |
298 | update(); | |
299 | repaint(); | |
300 | ||
301 | } | |
302 | ||
303 | } | |
304 | ||
305 | /** | |
306 | * A listener to watch for sizing of the component itself. | |
307 | * | |
308 | * @author Michael Baranov | |
309 | * | |
310 | */ | |
311 | private class SelfResizeListener implements ComponentListener { | |
312 | ||
313 | public void componentHidden(ComponentEvent e) { | |
314 | } | |
315 | ||
316 | public void componentMoved(ComponentEvent e) { | |
317 | } | |
318 | ||
319 | public void componentResized(ComponentEvent e) { | |
320 | update(); | |
321 | repaint(); | |
322 | } | |
323 | ||
324 | public void componentShown(ComponentEvent e) { | |
325 | update(); | |
326 | repaint(); | |
327 | } | |
328 | ||
329 | } | |
330 | ||
331 | /** | |
332 | * A listener to watch for mouse for the component itself. | |
333 | * | |
334 | * @author Michael Baranov | |
335 | * | |
336 | */ | |
337 | private class SelfMouseListener implements MouseListener, MouseMotionListener { | |
338 | ||
339 | public void mouseClicked(MouseEvent e) { | |
340 | } | |
341 | ||
342 | public void mouseEntered(MouseEvent e) { | |
343 | } | |
344 | ||
345 | public void mouseExited(MouseEvent e) { | |
346 | } | |
347 | ||
348 | public void mousePressed(MouseEvent e) { | |
349 | ||
350 | } | |
351 | ||
352 | public void mouseReleased(MouseEvent e) { | |
353 | if (paintRect != null) | |
354 | panRectTo(e.getPoint()); | |
355 | ||
356 | } | |
357 | ||
358 | public void mouseDragged(MouseEvent e) { | |
359 | if (paintRect != null) | |
360 | panRectTo(e.getPoint()); | |
361 | ||
362 | } | |
363 | ||
364 | public void mouseMoved(MouseEvent e) { | |
365 | } | |
366 | ||
367 | } | |
368 | ||
369 | /** | |
370 | * A listener to watch for graph property change, most important "scale". | |
371 | * | |
372 | * @author Michael Baranov | |
373 | * | |
374 | */ | |
375 | private class GraphPropertyChangeListener implements PropertyChangeListener { | |
376 | ||
377 | public void propertyChange(PropertyChangeEvent evt) { | |
378 | if ("scale".equals(evt.getPropertyName())) { | |
379 | update(); | |
380 | repaint(); | |
381 | } | |
382 | ||
383 | } | |
384 | ||
385 | } | |
386 | ||
387 | } |
0 | package com.michaelbaranov.microba.marker; | |
1 | ||
2 | import java.util.ArrayList; | |
3 | import java.util.List; | |
4 | ||
5 | import javax.swing.ListSelectionModel; | |
6 | ||
7 | import com.michaelbaranov.microba.common.AbstractBoundedTableModelWithSelection; | |
8 | ||
9 | /** | |
10 | * A basic implementation of <code>BoundedRangeModel</code>, | |
11 | * <code>ListSelectionModel</code> and <code>MutationModel</code> all in | |
12 | * one. Used by default by <code>JMarkerBar</code> as data model, selection | |
13 | * model and mutation model. | |
14 | * <p> | |
15 | * | |
16 | * | |
17 | * @author Michael Baranov | |
18 | * @see com.michaelbaranov.microba.marker.MarkerBar | |
19 | */ | |
20 | public class DefaultMarkerModel extends AbstractBoundedTableModelWithSelection | |
21 | implements MarkerMutationModel { | |
22 | ||
23 | private List data = new ArrayList(32); | |
24 | ||
25 | private int lowerBound = 0; | |
26 | ||
27 | private int upperBound = 100; | |
28 | ||
29 | /** | |
30 | * Constructs a <code>DefaultMarkerModel</code>. | |
31 | * | |
32 | */ | |
33 | public DefaultMarkerModel() { | |
34 | super(); | |
35 | setSelectionMode(ListSelectionModel.SINGLE_SELECTION); | |
36 | } | |
37 | ||
38 | /** | |
39 | * Returns the count of markers. Actually returns the cound of rows in the | |
40 | * <code>TableModel</code>. | |
41 | * | |
42 | * @return current marker count. | |
43 | */ | |
44 | public int getRowCount() { | |
45 | return data.size(); | |
46 | } | |
47 | ||
48 | /** | |
49 | * Returns the numbar of columns in the <code>TableModel</code>.This | |
50 | * implementation always returns 1. | |
51 | * | |
52 | * @return always 1. | |
53 | */ | |
54 | public int getColumnCount() { | |
55 | return 1; | |
56 | } | |
57 | ||
58 | /** | |
59 | * Returns the position of a marker, specified by index. | |
60 | * | |
61 | * @param rowIndex | |
62 | * marker index. | |
63 | * @param columnIndex | |
64 | * should be 0. | |
65 | * | |
66 | * @return an Integer position of the marker. | |
67 | * @see #setValueAt(Object, int, int) | |
68 | */ | |
69 | public Object getValueAt(int rowIndex, int columnIndex) { | |
70 | checkIndexes(rowIndex, columnIndex); | |
71 | return data.get(rowIndex); | |
72 | } | |
73 | ||
74 | /** | |
75 | * Assigns a new position to a marker, specified by index (moves the | |
76 | * marker). | |
77 | * <p> | |
78 | * This implementation does not check if the specified marker position lies | |
79 | * beyond current upper and lower bounds. | |
80 | * | |
81 | * @param aValue | |
82 | * an Integer new position. | |
83 | * @param rowIndex | |
84 | * marker index. | |
85 | * @param columnIndex | |
86 | * should be 0. | |
87 | * @see #getValueAt(int, int) | |
88 | */ | |
89 | public void setValueAt(Object aValue, int rowIndex, int columnIndex) { | |
90 | ||
91 | checkIndexes(rowIndex, columnIndex); | |
92 | ||
93 | Integer newValue = (Integer) aValue; | |
94 | Integer oldValue = (Integer) getValueAt(rowIndex, columnIndex); | |
95 | ||
96 | if (!newValue.equals(oldValue)) { | |
97 | data.remove(rowIndex); | |
98 | data.add(rowIndex, newValue); | |
99 | fireTableCellUpdated(rowIndex, columnIndex); | |
100 | } | |
101 | } | |
102 | ||
103 | /** | |
104 | * This implementation returns <code>Integer.class</code> for | |
105 | * columnIndex=0. | |
106 | * | |
107 | * @param columnIndex | |
108 | * should be 0. | |
109 | * | |
110 | * @return column data class. | |
111 | */ | |
112 | public Class getColumnClass(int columnIndex) { | |
113 | return Integer.class; | |
114 | } | |
115 | ||
116 | /** | |
117 | * This implementation returns <code>"Position"</code> for columnIndex=0. | |
118 | * | |
119 | * @param columnIndex | |
120 | * should be 0. | |
121 | * @return column name. | |
122 | */ | |
123 | public String getColumnName(int columnIndex) { | |
124 | return "Position"; | |
125 | } | |
126 | ||
127 | /** | |
128 | * This implementation always returns <code>true</code>, indicating that | |
129 | * all markers are movable. Override to change behaviour. | |
130 | * | |
131 | * @return always <code>true</code>. | |
132 | */ | |
133 | public boolean isCellEditable(int rowIndex, int columnIndex) { | |
134 | return true; | |
135 | } | |
136 | ||
137 | /** | |
138 | * Removes a marker, specified by index. Actually removes a row from the | |
139 | * <code>TableModel</code>. | |
140 | * | |
141 | * @param index | |
142 | * marker index. | |
143 | * @see #addMarkAtPosition(int) | |
144 | */ | |
145 | public void removeMarkerAtIndex(int index) { | |
146 | if (isSelectedIndex(index)) { | |
147 | removeSelectionInterval(index, index); | |
148 | } | |
149 | data.remove(index); | |
150 | fireTableRowsDeleted(index, index); | |
151 | } | |
152 | ||
153 | /** | |
154 | * Adds a marker with specified position. Actually appends a row to | |
155 | * <code>TableModel</code> with specified position value. Returns the | |
156 | * index of the added mark. | |
157 | * <p> | |
158 | * This implementation does not check if the specified marker position lies | |
159 | * beyond current upper and lower bounds. | |
160 | * | |
161 | * @return index of added mark. | |
162 | * @see #removeMarkerAtIndex(int) | |
163 | */ | |
164 | public int addMarkAtPosition(int position) { | |
165 | data.add(new Integer(position)); | |
166 | int row = data.size() - 1; | |
167 | fireTableRowsInserted(row, row); | |
168 | ||
169 | setSelectionInterval(row, row); | |
170 | return row; | |
171 | } | |
172 | ||
173 | /** | |
174 | * @inheritDoc | |
175 | * @see #setLowerBound(int) | |
176 | */ | |
177 | public int getLowerBound() { | |
178 | return lowerBound; | |
179 | } | |
180 | ||
181 | /** | |
182 | * @inheritDoc | |
183 | * @see #setUpperBound(int) | |
184 | */ | |
185 | public int getUpperBound() { | |
186 | return upperBound; | |
187 | } | |
188 | ||
189 | /** | |
190 | * Sets current lower bound. | |
191 | * | |
192 | * @param lowerBound | |
193 | * new lower bound value. | |
194 | * @see #setLowerBound(int) | |
195 | */ | |
196 | public void setLowerBound(int lowerBound) { | |
197 | if (lowerBound >= upperBound) | |
198 | throw new IllegalArgumentException("lowerBound >= upperBound"); | |
199 | ||
200 | int old = this.lowerBound; | |
201 | this.lowerBound = lowerBound; | |
202 | firePropertyChange(PROPERTY_LOWER_BOUND, old, lowerBound); | |
203 | } | |
204 | ||
205 | /** | |
206 | * Sets current upper bound. | |
207 | * | |
208 | * @param upperBound | |
209 | * new upper bound value. | |
210 | * @see #getUpperBound() | |
211 | */ | |
212 | public void setUpperBound(int upperBound) { | |
213 | int old = this.upperBound; | |
214 | this.upperBound = upperBound; | |
215 | firePropertyChange(PROPERTY_UPPER_BOUND, old, upperBound); | |
216 | } | |
217 | ||
218 | private void checkIndexes(int rowIndex, int columnIndex) { | |
219 | if (columnIndex != 0) | |
220 | throw new IndexOutOfBoundsException("columnIndex: " + columnIndex); | |
221 | ||
222 | if (rowIndex < 0 || rowIndex >= data.size()) | |
223 | throw new IndexOutOfBoundsException("rowIndex: " + rowIndex); | |
224 | } | |
225 | ||
226 | } |
0 | package com.michaelbaranov.microba.marker; | |
1 | ||
2 | import javax.swing.DefaultListSelectionModel; | |
3 | import javax.swing.ListSelectionModel; | |
4 | import javax.swing.SwingConstants; | |
5 | ||
6 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
7 | import com.michaelbaranov.microba.common.MicrobaComponent; | |
8 | import com.michaelbaranov.microba.marker.ui.MarkerBarUI; | |
9 | ||
10 | /** | |
11 | * A bar with multiple draggable position marks. | |
12 | * <p> | |
13 | * <strong>Features:</strong> | |
14 | * <ul> | |
15 | * <li>Single marker selection.</li> | |
16 | * <li>Respects unmovable marks.</li> | |
17 | * <li>Mouse marker selection & dragging.</li> | |
18 | * <li>Hirizontal & verical orientation.</li> | |
19 | * <li>Supported L&F: Metal, Windows, Motif, Basic for others.</li> | |
20 | * </ul> | |
21 | * | |
22 | * <strong>Implementation details:</strong> | |
23 | * <ul> | |
24 | * <li>Data model: | |
25 | * <code>{@link com.michaelbaranov.microba.common.BoundedTableModel}</code></li> | |
26 | * <li>Selection model: <code>{@link javax.swing.ListSelectionModel}</code></li> | |
27 | * <li>MutationModel model: | |
28 | * <code>{@link com.michaelbaranov.microba.marker.MarkerMutationModel}</code></li> | |
29 | * <li>UI delegate: | |
30 | * <code>{@link com.michaelbaranov.microba.marker.ui.MarkerBarUI}</code></li> | |
31 | * </ul> | |
32 | * <p> | |
33 | * | |
34 | * This implementation queries marker positions from a single table column of | |
35 | * the data model. The index defaults to 0, but you can specify it with <code> | |
36 | * {@link #setPositionColumn(int)}</code>. | |
37 | * A marker is considered to be unmovable, if corresponding table cell of the | |
38 | * data model is reported to be uneditable. | |
39 | * <p> | |
40 | * | |
41 | * This implementation determines the only currently selected marker with <code> | |
42 | * {@link ListSelectionModel#getLeadSelectionIndex()}</code> | |
43 | * of the selection model, so current selection model's selection mode has no | |
44 | * effect. | |
45 | * | |
46 | * @author Michael Baranov | |
47 | * | |
48 | */ | |
49 | public class MarkerBar extends MicrobaComponent { | |
50 | ||
51 | /** | |
52 | * The name of a "dataModel" property. | |
53 | */ | |
54 | public static final String PROPERTY_DATA_MODEL = "dataModel"; | |
55 | ||
56 | /** | |
57 | * The name of a "selectionModel" property. | |
58 | */ | |
59 | public static final String PROPERTY_SELECTION_MODEL = "selectionModel"; | |
60 | ||
61 | /** | |
62 | * The name of a "mutationModel" property. | |
63 | */ | |
64 | public static final String PROPERTY_MUTATION_MODEL = "mutationModel"; | |
65 | ||
66 | /** | |
67 | * The name of a "orientation" property. | |
68 | */ | |
69 | public static final String PROPERTY_ORIENTATION = "orientation"; | |
70 | ||
71 | /** | |
72 | * The name of a "positionColumn" property. | |
73 | */ | |
74 | public static final String PROPERTY_POSITION_COLUMN = "positionColumn"; | |
75 | ||
76 | /** | |
77 | * The name of a "colorColumn" property. | |
78 | */ | |
79 | public static final String PROPERTY_COLOR_COLUMN = "colorColumn"; | |
80 | ||
81 | /** | |
82 | * The name of a "fliped" property. | |
83 | */ | |
84 | public static final String PROPERTY_FLIP = "fliped"; | |
85 | ||
86 | private static final String uiClassID = "microba.MarkerBarUI"; | |
87 | ||
88 | private BoundedTableModel dataModel = null; | |
89 | ||
90 | private ListSelectionModel selectionModel = null; | |
91 | ||
92 | private MarkerMutationModel mutationModel = null; | |
93 | ||
94 | private int positionColumn = 0; | |
95 | ||
96 | private int colorColumn = -1; | |
97 | ||
98 | private int orientation = SwingConstants.HORIZONTAL; | |
99 | ||
100 | private boolean fliped = false; | |
101 | ||
102 | /** | |
103 | * Constructs a <code>MarkerBar</code> with all models set to a single | |
104 | * <code>DefaultMarkerModel.</code> | |
105 | * | |
106 | * @see DefaultMarkerModel | |
107 | */ | |
108 | public MarkerBar() { | |
109 | super(); | |
110 | DefaultMarkerModel markerModel = new DefaultMarkerModel(); | |
111 | dataModel = markerModel; | |
112 | selectionModel = markerModel; | |
113 | mutationModel = markerModel; | |
114 | setFocusable(true); | |
115 | updateUI(); | |
116 | } | |
117 | ||
118 | /** | |
119 | * Constructs a <code>MarkerBar</code> with given orientation. All models | |
120 | * set to a single <code>DefaultMarkerModel</code>. | |
121 | * | |
122 | * @param orientation | |
123 | * initial orientation. Possible values: | |
124 | * <code>SwingConstants.HORIZONTAL</code> or | |
125 | * <code>SwingConstants.VERTICAL</code> | |
126 | * @see DefaultMarkerModel | |
127 | */ | |
128 | public MarkerBar(int orientation) { | |
129 | super(); | |
130 | DefaultMarkerModel markerModel = new DefaultMarkerModel(); | |
131 | dataModel = markerModel; | |
132 | selectionModel = markerModel; | |
133 | mutationModel = markerModel; | |
134 | this.orientation = orientation; | |
135 | setFocusable(true); | |
136 | updateUI(); | |
137 | } | |
138 | ||
139 | /** | |
140 | * Constructs a <code>MarkerBar</code> with given data model, a | |
141 | * <code>DefaultListSelectionModel<code> as selection model and no mutation model. | |
142 | * | |
143 | * @param dataModel | |
144 | * initial data model. May be <code>null<code> | |
145 | * | |
146 | * @see BoundedTableModel | |
147 | */ | |
148 | public MarkerBar(BoundedTableModel dataModel) { | |
149 | super(); | |
150 | this.dataModel = dataModel; | |
151 | selectionModel = new DefaultListSelectionModel(); | |
152 | mutationModel = null; | |
153 | setFocusable(true); | |
154 | updateUI(); | |
155 | } | |
156 | ||
157 | /** | |
158 | * Constructs a <code>MarkerBar</code> with given data model and selection | |
159 | * model. No mutation model. | |
160 | * | |
161 | * @param dataModel | |
162 | * initial data model. May be <code>null<code>; | |
163 | * @param selectionModel initial selection model. | |
164 | * @see BoundedTableModel | |
165 | */ | |
166 | public MarkerBar(BoundedTableModel dataModel, ListSelectionModel selectionModel) { | |
167 | super(); | |
168 | this.dataModel = dataModel; | |
169 | this.selectionModel = selectionModel; | |
170 | mutationModel = null; | |
171 | setFocusable(true); | |
172 | updateUI(); | |
173 | } | |
174 | ||
175 | /** | |
176 | * Look&Feel UI delegate key (classID). This implementation returns: | |
177 | * <code>"MarkerBarUI"<code>. | |
178 | */ | |
179 | public String getUIClassID() { | |
180 | return uiClassID; | |
181 | } | |
182 | ||
183 | /** | |
184 | * Returns current data model. | |
185 | * | |
186 | * @return current BoundedTableModel. | |
187 | * @see #setDataModel(BoundedTableModel) | |
188 | * @see BoundedTableModel | |
189 | */ | |
190 | public BoundedTableModel getDataModel() { | |
191 | return dataModel; | |
192 | } | |
193 | ||
194 | /** | |
195 | * Replaces current data model with specified one. This implementation uses | |
196 | * current position column index to query marker positions. | |
197 | * | |
198 | * @param model | |
199 | * new data model. | |
200 | * @see #getDataModel() | |
201 | * @see #getPositionColumn() | |
202 | * @see BoundedTableModel | |
203 | */ | |
204 | public void setDataModel(BoundedTableModel model) { | |
205 | BoundedTableModel oldModel = this.dataModel; | |
206 | this.dataModel = model; | |
207 | firePropertyChange(PROPERTY_DATA_MODEL, oldModel, model); | |
208 | } | |
209 | ||
210 | /** | |
211 | * Returns current component orientation. | |
212 | * | |
213 | * @return current component orientation. | |
214 | * @see #setOrientation(int) | |
215 | */ | |
216 | public int getOrientation() { | |
217 | return orientation; | |
218 | } | |
219 | ||
220 | /** | |
221 | * Re-orientates the component. | |
222 | * | |
223 | * @param orientation | |
224 | * new orientation value. Possible values: | |
225 | * <code>{@link SwingConstants#HORIZONTAL}</code> or | |
226 | * <code>{@link SwingConstants#VERTICAL}</code> | |
227 | * @see #getOrientation() | |
228 | */ | |
229 | public void setOrientation(int orientation) { | |
230 | int oldOrientation = this.orientation; | |
231 | this.orientation = orientation; | |
232 | firePropertyChange(PROPERTY_ORIENTATION, oldOrientation, orientation); | |
233 | } | |
234 | ||
235 | /** | |
236 | * Returns current mutation model. | |
237 | * | |
238 | * @return current MutationModel. | |
239 | * @see #setMutationModel(MarkerMutationModel) | |
240 | * @see MarkerMutationModel | |
241 | */ | |
242 | public MarkerMutationModel getMutationModel() { | |
243 | return mutationModel; | |
244 | } | |
245 | ||
246 | /** | |
247 | * Replaces current mutation model with given one. | |
248 | * | |
249 | * @param mutationModel | |
250 | * new mutation model. May be <code>null<code>. | |
251 | * @see #getMutationModel() | |
252 | * @see MarkerMutationModel | |
253 | */ | |
254 | public void setMutationModel(MarkerMutationModel mutationModel) { | |
255 | MarkerMutationModel oldMutationModel = this.mutationModel; | |
256 | this.mutationModel = mutationModel; | |
257 | firePropertyChange(PROPERTY_MUTATION_MODEL, oldMutationModel, mutationModel); | |
258 | } | |
259 | ||
260 | /** | |
261 | * Returns current selection model. | |
262 | * | |
263 | * @return current ListSelectionModel. | |
264 | * @see #setSelectionModel(ListSelectionModel) | |
265 | */ | |
266 | public ListSelectionModel getSelectionModel() { | |
267 | return selectionModel; | |
268 | } | |
269 | ||
270 | /** | |
271 | * Replaces current selection model with given one. This implementation uses | |
272 | * <code>{@link ListSelectionModel#getLeadSelectionIndex()}</code> to | |
273 | * determine selected marker. | |
274 | * | |
275 | * @param selectionModel | |
276 | * new selection model. May be <code>null<code>. | |
277 | * | |
278 | * @see #getSelectionModel() | |
279 | */ | |
280 | public void setSelectionModel(ListSelectionModel selectionModel) { | |
281 | ListSelectionModel oldSelectionModel = this.selectionModel; | |
282 | this.selectionModel = selectionModel; | |
283 | firePropertyChange(PROPERTY_SELECTION_MODEL, oldSelectionModel, selectionModel); | |
284 | } | |
285 | ||
286 | /** | |
287 | * Returns an index of currently used table column to query marker position. | |
288 | * | |
289 | * @return current position column index. | |
290 | * @see #setPositionColumn(int) | |
291 | */ | |
292 | public int getPositionColumn() { | |
293 | return positionColumn; | |
294 | } | |
295 | ||
296 | /** | |
297 | * Sets the index of the data model table column used to query marker | |
298 | * position. | |
299 | * | |
300 | * @param positionColumn | |
301 | * new position column index. | |
302 | * @see #getPositionColumn() | |
303 | */ | |
304 | public void setPositionColumn(int positionColumn) { | |
305 | int oldDataColumn = this.positionColumn; | |
306 | this.positionColumn = positionColumn; | |
307 | firePropertyChange(PROPERTY_POSITION_COLUMN, oldDataColumn, positionColumn); | |
308 | } | |
309 | ||
310 | /** | |
311 | * Returns an index of currently used table column to query marker color. | |
312 | * Defaults to -1, which means not to query data model for color. | |
313 | * | |
314 | * @return current color column index. | |
315 | * @see #setColorColumn(int) | |
316 | */ | |
317 | public int getColorColumn() { | |
318 | return colorColumn; | |
319 | } | |
320 | ||
321 | /** | |
322 | * Sets the index of the data model table column used to query marker color. | |
323 | * Set to -1 in order not to query data model for color data. | |
324 | * | |
325 | * @param colorColumn | |
326 | * new color column index. | |
327 | * @see #getColorColumn() | |
328 | */ | |
329 | public void setColorColumn(int colorColumn) { | |
330 | int old = this.colorColumn; | |
331 | this.colorColumn = colorColumn; | |
332 | firePropertyChange(PROPERTY_COLOR_COLUMN, old, colorColumn); | |
333 | } | |
334 | ||
335 | /** | |
336 | * Returns a distance in pixeld between the edge of the component (left & | |
337 | * right edge for horizontal orientation, top & bottom edge for vertical) | |
338 | * and a marker beak point in outermost position. The value is actually | |
339 | * queried from current UI delegate. | |
340 | * | |
341 | * @return Gap value. | |
342 | * @see MarkerBarUI | |
343 | */ | |
344 | public int getMarkerSideGap() { | |
345 | return ((MarkerBarUI) getUI()).getMarkerSideGap(); | |
346 | } | |
347 | ||
348 | /** | |
349 | * Returns current flip flag value. | |
350 | * <p> | |
351 | * The flip flag defines where marker bicks are pointed:<br> | |
352 | * <code>true</code>: down for horizontal orientation, left for vertical.<br> | |
353 | * <code>false</code>: up for horizontal orientation, right for vertical. | |
354 | * | |
355 | * @return current flip value. | |
356 | */ | |
357 | public boolean isFliped() { | |
358 | return fliped; | |
359 | } | |
360 | ||
361 | /** | |
362 | * Set flip flag value. | |
363 | * <p> | |
364 | * The flip flag defines where marker bicks are pointed:<br> | |
365 | * <code>true</code>: down for horizontal orientation, left for vertical.<br> | |
366 | * <code>false</code>: up for horizontal orientation, right for vertical. | |
367 | * | |
368 | * @param flip | |
369 | * new flip flag value. | |
370 | */ | |
371 | public void setFliped(boolean flip) { | |
372 | boolean old = this.fliped; | |
373 | this.fliped = flip; | |
374 | firePropertyChange(PROPERTY_FLIP, old, flip); | |
375 | } | |
376 | ||
377 | } |
0 | package com.michaelbaranov.microba.marker; | |
1 | ||
2 | /** | |
3 | * A callback interface used exclusively by | |
4 | * <code>{@link com.michaelbaranov.microba.marker.MarkerBar}</code> as a | |
5 | * mutation model. | |
6 | * <p> | |
7 | * The <code>JMarkerBar</code> class notifies an implementor of the | |
8 | * <code>MutationModel</code> about the fact, that the user is requesting a | |
9 | * marker to be removed or added. | |
10 | * | |
11 | * @author Michael Baranov | |
12 | * @see com.michaelbaranov.microba.marker.MarkerBar | |
13 | */ | |
14 | public interface MarkerMutationModel { | |
15 | ||
16 | /** | |
17 | * Called when the user requests a mark to be removed from | |
18 | * <code>JMarkerBar</code>. | |
19 | * | |
20 | * @param index | |
21 | * index of the mark to be removed. | |
22 | */ | |
23 | void removeMarkerAtIndex(int index); | |
24 | ||
25 | /** | |
26 | * Called when the user requests a mark to be inserted into | |
27 | * <code>JMarkerBar</code>. | |
28 | * | |
29 | * @param position | |
30 | * position at which to insert the mark. | |
31 | * @return index of newly added mark. | |
32 | */ | |
33 | int addMarkAtPosition(int position); | |
34 | } |
0 | package com.michaelbaranov.microba.marker.ui; | |
1 | ||
2 | import java.awt.Polygon; | |
3 | import java.awt.event.ActionEvent; | |
4 | import java.awt.event.ComponentEvent; | |
5 | import java.awt.event.ComponentListener; | |
6 | import java.awt.event.FocusEvent; | |
7 | import java.awt.event.FocusListener; | |
8 | import java.awt.event.KeyEvent; | |
9 | import java.awt.event.MouseEvent; | |
10 | import java.awt.event.MouseListener; | |
11 | import java.awt.event.MouseMotionListener; | |
12 | import java.beans.PropertyChangeEvent; | |
13 | import java.beans.PropertyChangeListener; | |
14 | ||
15 | import javax.swing.AbstractAction; | |
16 | import javax.swing.JComponent; | |
17 | import javax.swing.KeyStroke; | |
18 | import javax.swing.ListSelectionModel; | |
19 | import javax.swing.SwingConstants; | |
20 | import javax.swing.SwingUtilities; | |
21 | import javax.swing.event.ListSelectionEvent; | |
22 | import javax.swing.event.ListSelectionListener; | |
23 | import javax.swing.event.TableModelEvent; | |
24 | import javax.swing.event.TableModelListener; | |
25 | ||
26 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
27 | import com.michaelbaranov.microba.marker.MarkerBar; | |
28 | import com.michaelbaranov.microba.marker.MarkerMutationModel; | |
29 | import com.michaelbaranov.microba.marker.ui.basic.BasicMarkerBarUI; | |
30 | ||
31 | public class MarkerBarListener implements TableModelListener, MouseListener, | |
32 | MouseMotionListener, FocusListener, PropertyChangeListener, | |
33 | ComponentListener, ListSelectionListener { | |
34 | ||
35 | private static final String VK_DELETE_KEY = "##VK_DELETE##"; | |
36 | ||
37 | private static final KeyStroke DELETE_KEYSTROKE = KeyStroke.getKeyStroke( | |
38 | KeyEvent.VK_DELETE, 0); | |
39 | ||
40 | private final BasicMarkerBarUI barUI; | |
41 | ||
42 | private MarkerBar bar; | |
43 | ||
44 | private int holdingIndex = -1; | |
45 | ||
46 | private int holdingShift; | |
47 | ||
48 | public MarkerBarListener(BasicMarkerBarUI barUI, MarkerBar markerBar) { | |
49 | this.barUI = barUI; | |
50 | this.bar = markerBar; | |
51 | } | |
52 | ||
53 | public void mouseClicked(MouseEvent e) { | |
54 | } | |
55 | ||
56 | public void mousePressed(MouseEvent e) { | |
57 | ||
58 | if (!bar.isEnabled()) | |
59 | return; | |
60 | ||
61 | this.barUI.calculateViewRectAndBaseline(bar); | |
62 | ||
63 | bar.requestFocusInWindow(); | |
64 | ||
65 | BoundedTableModel dataModel = bar.getDataModel(); | |
66 | ListSelectionModel selectionModel = bar.getSelectionModel(); | |
67 | int dataColumn = bar.getPositionColumn(); | |
68 | ||
69 | if (SwingUtilities.isLeftMouseButton(e) && selectionModel != null | |
70 | && dataModel != null) { | |
71 | handleLMBDown(e, dataModel, selectionModel, dataColumn); | |
72 | } | |
73 | if (SwingUtilities.isRightMouseButton(e) && dataModel != null) { | |
74 | handleRMBDown(e, dataModel); | |
75 | } | |
76 | ||
77 | } | |
78 | ||
79 | private void handleRMBDown(MouseEvent e, BoundedTableModel dataModel) { | |
80 | int logicalOffset; | |
81 | if (bar.getOrientation() == SwingConstants.HORIZONTAL) | |
82 | logicalOffset = this.barUI.componentOffsetToLogicalOffset(e.getX(), | |
83 | dataModel); | |
84 | else | |
85 | logicalOffset = this.barUI.componentOffsetToLogicalOffset(e.getY(), | |
86 | dataModel); | |
87 | int logicalPos = dataModel.getLowerBound() + logicalOffset; | |
88 | ||
89 | if (logicalPos >= dataModel.getLowerBound() | |
90 | && logicalPos <= dataModel.getUpperBound()) { | |
91 | ||
92 | MarkerMutationModel mutationModel = bar.getMutationModel(); | |
93 | if (mutationModel != null) | |
94 | mutationModel.addMarkAtPosition(logicalPos); | |
95 | ||
96 | } | |
97 | } | |
98 | ||
99 | private void handleLMBDown(MouseEvent e, BoundedTableModel dataModel, | |
100 | ListSelectionModel selectionModel, int dataColumn) { | |
101 | ||
102 | int numAreas = dataModel.getRowCount(); | |
103 | Polygon areas[] = this.barUI.calculateMarkerAreas(bar); | |
104 | ||
105 | // try already selected | |
106 | for (int i = 0; i < numAreas; i++) { | |
107 | Polygon p = areas[i]; | |
108 | if (p.contains(e.getPoint()) && selectionModel.isSelectedIndex(i) | |
109 | && dataModel.isCellEditable(i, dataColumn)) { | |
110 | holdingIndex = i; | |
111 | return; | |
112 | } | |
113 | } | |
114 | // try editable | |
115 | for (int i = 0; i < numAreas; i++) { | |
116 | Polygon p = areas[i]; | |
117 | if (p.contains(e.getPoint()) | |
118 | && dataModel.isCellEditable(i, dataColumn)) { | |
119 | selectionModel.setSelectionInterval(i, i); | |
120 | holdingIndex = i; | |
121 | if (bar.getOrientation() == SwingConstants.HORIZONTAL) | |
122 | holdingShift = e.getX() - p.xpoints[0]; | |
123 | else | |
124 | holdingShift = e.getY() - p.ypoints[0]; | |
125 | return; | |
126 | } | |
127 | } | |
128 | // try other | |
129 | for (int i = 0; i < numAreas; i++) { | |
130 | Polygon p = areas[i]; | |
131 | if (p.contains(e.getPoint())) { | |
132 | selectionModel.setSelectionInterval(i, i); | |
133 | holdingIndex = -1; | |
134 | return; | |
135 | } | |
136 | } | |
137 | selectionModel.clearSelection(); | |
138 | } | |
139 | ||
140 | public void mouseReleased(MouseEvent e) { | |
141 | holdingIndex = -1; | |
142 | ||
143 | } | |
144 | ||
145 | public void mouseEntered(MouseEvent e) { | |
146 | ||
147 | } | |
148 | ||
149 | public void mouseExited(MouseEvent e) { | |
150 | ||
151 | } | |
152 | ||
153 | public void mouseDragged(MouseEvent e) { | |
154 | ||
155 | if (!bar.isEnabled()) | |
156 | return; | |
157 | ||
158 | BoundedTableModel dataModel = bar.getDataModel(); | |
159 | if (holdingIndex >= 0 && dataModel != null) { | |
160 | int componentOffset; | |
161 | if (bar.getOrientation() == SwingConstants.HORIZONTAL) | |
162 | componentOffset = e.getX() - holdingShift; | |
163 | else | |
164 | componentOffset = e.getY() - holdingShift; | |
165 | ||
166 | int logicalOffset = this.barUI.componentOffsetToLogicalOffset( | |
167 | componentOffset, dataModel); | |
168 | int logicalPos = dataModel.getLowerBound() + logicalOffset; | |
169 | ||
170 | if (logicalPos < dataModel.getLowerBound()) | |
171 | logicalPos = dataModel.getLowerBound(); | |
172 | if (logicalPos > dataModel.getUpperBound()) | |
173 | logicalPos = dataModel.getUpperBound(); | |
174 | ||
175 | dataModel.setValueAt(new Integer(logicalPos), holdingIndex, bar | |
176 | .getPositionColumn()); | |
177 | } | |
178 | ||
179 | } | |
180 | ||
181 | public void mouseMoved(MouseEvent e) { | |
182 | ||
183 | } | |
184 | ||
185 | public void focusGained(FocusEvent e) { | |
186 | bar.repaint(); | |
187 | ||
188 | } | |
189 | ||
190 | public void focusLost(FocusEvent e) { | |
191 | bar.repaint(); | |
192 | ||
193 | } | |
194 | ||
195 | public void propertyChange(PropertyChangeEvent evt) { | |
196 | ||
197 | if (evt.getSource() instanceof MarkerBar) { | |
198 | ||
199 | if (MarkerBar.PROPERTY_DATA_MODEL.equals(evt.getPropertyName())) { | |
200 | BoundedTableModel oldModel = (BoundedTableModel) evt | |
201 | .getOldValue(); | |
202 | BoundedTableModel newModel = (BoundedTableModel) evt | |
203 | .getNewValue(); | |
204 | ||
205 | if (oldModel != null) | |
206 | oldModel.removeTableModelListener(this); | |
207 | if (newModel != null) | |
208 | newModel.addTableModelListener(this); | |
209 | ||
210 | holdingIndex = -1; | |
211 | bar.revalidate(); | |
212 | } | |
213 | if (MarkerBar.PROPERTY_SELECTION_MODEL.equals(evt | |
214 | .getPropertyName())) { | |
215 | ListSelectionModel oldModel = (ListSelectionModel) evt | |
216 | .getOldValue(); | |
217 | ListSelectionModel newModel = (ListSelectionModel) evt | |
218 | .getNewValue(); | |
219 | ||
220 | if (oldModel != null) | |
221 | oldModel.removeListSelectionListener(this); | |
222 | if (newModel != null) | |
223 | newModel.addListSelectionListener(this); | |
224 | ||
225 | holdingIndex = -1; | |
226 | bar.repaint(); | |
227 | } | |
228 | if (MarkerBar.PROPERTY_MUTATION_MODEL | |
229 | .equals(evt.getPropertyName())) { | |
230 | // do not care | |
231 | } | |
232 | if (MarkerBar.PROPERTY_ORIENTATION.equals(evt.getPropertyName())) { | |
233 | holdingIndex = -1; | |
234 | bar.revalidate(); | |
235 | } | |
236 | if (MarkerBar.PROPERTY_POSITION_COLUMN.equals(evt | |
237 | .getPropertyName())) { | |
238 | holdingIndex = -1; | |
239 | bar.repaint(); | |
240 | } | |
241 | if ("enabled".equals(evt.getPropertyName())) { | |
242 | holdingIndex = -1; | |
243 | bar.repaint(); | |
244 | } | |
245 | } | |
246 | if (evt.getSource() instanceof BoundedTableModel) { | |
247 | bar.revalidate(); | |
248 | } | |
249 | ||
250 | } | |
251 | ||
252 | public void installKeyboardActions(JComponent c) { | |
253 | ||
254 | c.getInputMap().put(DELETE_KEYSTROKE, VK_DELETE_KEY); | |
255 | c.getActionMap().put(VK_DELETE_KEY, new AbstractAction() { | |
256 | ||
257 | public void actionPerformed(ActionEvent e) { | |
258 | ||
259 | BoundedTableModel dataModel = bar.getDataModel(); | |
260 | ListSelectionModel selectionModel = bar.getSelectionModel(); | |
261 | MarkerMutationModel mutationModel = bar.getMutationModel(); | |
262 | ||
263 | if (selectionModel != null && dataModel != null | |
264 | && mutationModel != null | |
265 | && !selectionModel.isSelectionEmpty()) { | |
266 | int selected = selectionModel.getLeadSelectionIndex(); | |
267 | if (dataModel.isCellEditable(selected, bar | |
268 | .getPositionColumn())) { | |
269 | mutationModel.removeMarkerAtIndex(selected); | |
270 | } | |
271 | } | |
272 | } | |
273 | }); | |
274 | } | |
275 | ||
276 | public void uninstallKeyboardActions(JComponent c) { | |
277 | c.getActionMap().remove(VK_DELETE_KEY); | |
278 | c.getInputMap().remove(DELETE_KEYSTROKE); | |
279 | } | |
280 | ||
281 | public void componentResized(ComponentEvent e) { | |
282 | ||
283 | } | |
284 | ||
285 | public void componentMoved(ComponentEvent e) { | |
286 | ||
287 | } | |
288 | ||
289 | public void componentShown(ComponentEvent e) { | |
290 | ||
291 | } | |
292 | ||
293 | public void componentHidden(ComponentEvent e) { | |
294 | ||
295 | } | |
296 | ||
297 | public void tableChanged(TableModelEvent e) { | |
298 | bar.repaint(); | |
299 | ||
300 | } | |
301 | ||
302 | public void valueChanged(ListSelectionEvent e) { | |
303 | bar.repaint(); | |
304 | ||
305 | } | |
306 | }⏎ |
0 | package com.michaelbaranov.microba.marker.ui; | |
1 | ||
2 | import java.awt.Insets; | |
3 | import java.awt.Polygon; | |
4 | import java.awt.Rectangle; | |
5 | ||
6 | import javax.swing.SwingConstants; | |
7 | import javax.swing.plaf.ComponentUI; | |
8 | ||
9 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
10 | import com.michaelbaranov.microba.marker.MarkerBar; | |
11 | ||
12 | public class MarkerBarUI extends ComponentUI { | |
13 | ||
14 | protected int MARKER_BODY_WIDTH = 12; | |
15 | ||
16 | protected int MARKER_BODY_HEIGHT = 12; | |
17 | ||
18 | protected int MARKER_BICK_HEIGHT = 6; | |
19 | ||
20 | protected int baselineLeft; | |
21 | ||
22 | protected int baselineTop; | |
23 | ||
24 | protected int baselineLength; | |
25 | ||
26 | protected Rectangle viewRect = new Rectangle(); | |
27 | ||
28 | protected Polygon[] polys = new Polygon[0]; | |
29 | ||
30 | protected Polygon[] calculateMarkerAreas(MarkerBar bar) { | |
31 | ||
32 | BoundedTableModel model = bar.getDataModel(); | |
33 | ||
34 | int count = model.getRowCount(); | |
35 | ||
36 | if (polys.length < count) { | |
37 | // grow shared polygon array | |
38 | polys = new Polygon[count * 2]; | |
39 | for (int i = 0; i < polys.length; i++) { | |
40 | int xx[] = new int[5]; | |
41 | int yy[] = new int[5]; | |
42 | Polygon p = new Polygon(xx, yy, 5); | |
43 | polys[i] = p; | |
44 | } | |
45 | } | |
46 | ||
47 | for (int i = 0; i < count; i++) { | |
48 | int intValueAt = ((Integer) model.getValueAt(i, bar | |
49 | .getPositionColumn())).intValue(); | |
50 | ||
51 | int logicalOffset = intValueAt - model.getLowerBound(); | |
52 | ||
53 | int baselineOffset = logicalOffsetToBaselineOffset(logicalOffset, | |
54 | model); | |
55 | int xx[] = polys[i].xpoints; | |
56 | int yy[] = polys[i].ypoints; | |
57 | ||
58 | if (bar.getOrientation() == SwingConstants.HORIZONTAL) { | |
59 | ||
60 | int x = baselineLeft + baselineOffset; | |
61 | if (bar.isFliped()) { | |
62 | xx[0] = x; | |
63 | yy[0] = baselineTop; | |
64 | ||
65 | xx[1] = x + MARKER_BODY_WIDTH / 2; | |
66 | yy[1] = baselineTop + MARKER_BICK_HEIGHT; | |
67 | ||
68 | xx[2] = x + MARKER_BODY_WIDTH / 2; | |
69 | yy[2] = baselineTop + MARKER_BICK_HEIGHT | |
70 | + MARKER_BODY_HEIGHT; | |
71 | ||
72 | xx[3] = x - MARKER_BODY_WIDTH / 2; | |
73 | yy[3] = baselineTop + MARKER_BICK_HEIGHT | |
74 | + MARKER_BODY_HEIGHT; | |
75 | ||
76 | xx[4] = x - MARKER_BODY_WIDTH / 2; | |
77 | yy[4] = baselineTop + MARKER_BICK_HEIGHT; | |
78 | ||
79 | } else { | |
80 | ||
81 | xx[0] = x; | |
82 | yy[0] = baselineTop; | |
83 | ||
84 | xx[1] = x + MARKER_BODY_WIDTH / 2; | |
85 | yy[1] = baselineTop - MARKER_BICK_HEIGHT; | |
86 | ||
87 | xx[2] = x + MARKER_BODY_WIDTH / 2; | |
88 | yy[2] = baselineTop - MARKER_BICK_HEIGHT | |
89 | - MARKER_BODY_HEIGHT; | |
90 | ||
91 | xx[3] = x - MARKER_BODY_WIDTH / 2; | |
92 | yy[3] = baselineTop - MARKER_BICK_HEIGHT | |
93 | - MARKER_BODY_HEIGHT; | |
94 | ||
95 | xx[4] = x - MARKER_BODY_WIDTH / 2; | |
96 | yy[4] = baselineTop - MARKER_BICK_HEIGHT; | |
97 | } | |
98 | ||
99 | } else { | |
100 | int y = baselineLeft + baselineOffset; | |
101 | ||
102 | if (bar.isFliped()) { | |
103 | xx[0] = baselineTop; | |
104 | yy[0] = y; | |
105 | ||
106 | yy[1] = y + MARKER_BODY_WIDTH / 2; | |
107 | xx[1] = baselineTop - MARKER_BICK_HEIGHT; | |
108 | ||
109 | yy[2] = y + MARKER_BODY_WIDTH / 2; | |
110 | xx[2] = baselineTop - MARKER_BICK_HEIGHT | |
111 | - MARKER_BODY_HEIGHT; | |
112 | ||
113 | yy[3] = y - MARKER_BODY_WIDTH / 2; | |
114 | xx[3] = baselineTop - MARKER_BICK_HEIGHT | |
115 | - MARKER_BODY_HEIGHT; | |
116 | ||
117 | yy[4] = y - MARKER_BODY_WIDTH / 2; | |
118 | xx[4] = baselineTop - MARKER_BICK_HEIGHT; | |
119 | } else { | |
120 | ||
121 | xx[0] = baselineTop; | |
122 | yy[0] = y; | |
123 | ||
124 | yy[1] = y + MARKER_BODY_WIDTH / 2; | |
125 | xx[1] = baselineTop + MARKER_BICK_HEIGHT; | |
126 | ||
127 | yy[2] = y + MARKER_BODY_WIDTH / 2; | |
128 | xx[2] = baselineTop + MARKER_BICK_HEIGHT | |
129 | + MARKER_BODY_HEIGHT; | |
130 | ||
131 | yy[3] = y - MARKER_BODY_WIDTH / 2; | |
132 | xx[3] = baselineTop + MARKER_BICK_HEIGHT | |
133 | + MARKER_BODY_HEIGHT; | |
134 | ||
135 | yy[4] = y - MARKER_BODY_WIDTH / 2; | |
136 | xx[4] = baselineTop + MARKER_BICK_HEIGHT; | |
137 | } | |
138 | ||
139 | } | |
140 | polys[i].invalidate(); | |
141 | ||
142 | } | |
143 | return polys; | |
144 | } | |
145 | ||
146 | protected int logicalOffsetToBaselineOffset(int logicalOffset, | |
147 | BoundedTableModel model) { | |
148 | int positionRange = model.getUpperBound() - model.getLowerBound(); | |
149 | return logicalOffset * baselineLength / positionRange; | |
150 | } | |
151 | ||
152 | protected int baselineOffsetToLogicalOffset(int baselineOffset, | |
153 | BoundedTableModel model) { | |
154 | int positionRange = model.getUpperBound() - model.getLowerBound(); | |
155 | return baselineOffset * positionRange / baselineLength; | |
156 | } | |
157 | ||
158 | protected int componentOffsetToLogicalOffset(int componentOffsetLeft, | |
159 | BoundedTableModel dataModel) { | |
160 | return baselineOffsetToLogicalOffset( | |
161 | componentOffsetLeft - baselineLeft, dataModel); | |
162 | } | |
163 | ||
164 | protected void calculateViewRectAndBaseline(MarkerBar bar) { | |
165 | Insets insets = bar.getInsets(); | |
166 | viewRect.x = insets.left; | |
167 | viewRect.y = insets.top; | |
168 | viewRect.width = bar.getWidth() - (insets.right + viewRect.x); | |
169 | viewRect.height = bar.getHeight() - (insets.bottom + viewRect.y); | |
170 | ||
171 | if (bar.getOrientation() == SwingConstants.HORIZONTAL) { | |
172 | baselineLeft = viewRect.x + MARKER_BODY_WIDTH / 2; | |
173 | baselineLength = viewRect.width - MARKER_BODY_WIDTH - 1; | |
174 | if (bar.isFliped()) { | |
175 | baselineTop = viewRect.y; | |
176 | } else { | |
177 | baselineTop = viewRect.y + viewRect.height - 1; | |
178 | } | |
179 | } else { | |
180 | baselineLeft = viewRect.y + MARKER_BODY_WIDTH / 2; | |
181 | baselineLength = viewRect.height - MARKER_BODY_WIDTH - 1; | |
182 | if (bar.isFliped()) { | |
183 | baselineTop = viewRect.x + viewRect.width - 1; | |
184 | } else { | |
185 | baselineTop = viewRect.x; | |
186 | } | |
187 | } | |
188 | } | |
189 | ||
190 | public int getMarkerSideGap() { | |
191 | return MARKER_BODY_WIDTH / 2; | |
192 | } | |
193 | ||
194 | } |
0 | package com.michaelbaranov.microba.marker.ui.basic; | |
1 | ||
2 | import java.awt.Color; | |
3 | import java.awt.Dimension; | |
4 | import java.awt.Graphics; | |
5 | import java.awt.Insets; | |
6 | import java.awt.Polygon; | |
7 | import java.awt.Rectangle; | |
8 | import java.awt.event.MouseMotionListener; | |
9 | ||
10 | import javax.swing.JComponent; | |
11 | import javax.swing.ListSelectionModel; | |
12 | import javax.swing.LookAndFeel; | |
13 | import javax.swing.SwingConstants; | |
14 | import javax.swing.UIManager; | |
15 | import javax.swing.plaf.ComponentUI; | |
16 | import javax.swing.plaf.basic.BasicGraphicsUtils; | |
17 | ||
18 | import com.michaelbaranov.microba.common.BoundedTableModel; | |
19 | import com.michaelbaranov.microba.marker.MarkerBar; | |
20 | import com.michaelbaranov.microba.marker.ui.MarkerBarListener; | |
21 | import com.michaelbaranov.microba.marker.ui.MarkerBarUI; | |
22 | ||
23 | public class BasicMarkerBarUI extends MarkerBarUI { | |
24 | ||
25 | private Color selectionColor; | |
26 | ||
27 | private Color shadowColor; | |
28 | ||
29 | private Color focusColor; | |
30 | ||
31 | private Color disabledColor; | |
32 | ||
33 | private Color normalColor; | |
34 | ||
35 | private Color textColor; | |
36 | ||
37 | public static ComponentUI createUI(JComponent c) { | |
38 | return new BasicMarkerBarUI(); | |
39 | } | |
40 | ||
41 | public void installUI(JComponent component) { | |
42 | installListeners((MarkerBar) component); | |
43 | installKeyboardActions((MarkerBar) component); | |
44 | installDefaults((MarkerBar) component); | |
45 | } | |
46 | ||
47 | public void uninstallUI(JComponent component) { | |
48 | uninstallListeners((MarkerBar) component); | |
49 | uninstallKeyboardActions((MarkerBar) component); | |
50 | } | |
51 | ||
52 | protected void installDefaults(MarkerBar bar) { | |
53 | LookAndFeel.installBorder(bar, "Slider.border"); | |
54 | LookAndFeel.installColors(bar, "Slider.background", "Slider.foreground"); | |
55 | ||
56 | // selectedmark | |
57 | selectionColor = UIManager.getColor("ComboBox.selectionBackground"); | |
58 | // fixed mark | |
59 | disabledColor = UIManager.getColor("ComboBox.disabledBackground"); | |
60 | normalColor = UIManager.getColor("ComboBox.background"); | |
61 | // focus rect | |
62 | focusColor = UIManager.getColor("Slider.focus"); | |
63 | // text & outline color | |
64 | textColor = UIManager.getColor("textText"); | |
65 | ||
66 | } | |
67 | ||
68 | protected void installKeyboardActions(MarkerBar bar) { | |
69 | MarkerBarListener listener = lookupListsner(bar); | |
70 | if (listener != null) | |
71 | listener.installKeyboardActions(bar); | |
72 | ||
73 | } | |
74 | ||
75 | protected void uninstallKeyboardActions(MarkerBar bar) { | |
76 | MarkerBarListener listener = lookupListsner(bar); | |
77 | if (listener != null) | |
78 | listener.uninstallKeyboardActions(bar); | |
79 | ||
80 | } | |
81 | ||
82 | protected void installListeners(MarkerBar markerBar) { | |
83 | MarkerBarListener listener = createListener(markerBar); | |
84 | ||
85 | if (markerBar.getDataModel() != null) | |
86 | markerBar.getDataModel().addTableModelListener(listener); | |
87 | if (markerBar.getSelectionModel() != null) | |
88 | markerBar.getSelectionModel().addListSelectionListener(listener); | |
89 | markerBar.addMouseListener(listener); | |
90 | markerBar.addMouseMotionListener(listener); | |
91 | markerBar.addFocusListener(listener); | |
92 | markerBar.addPropertyChangeListener(listener); | |
93 | markerBar.addComponentListener(listener); | |
94 | } | |
95 | ||
96 | protected void uninstallListeners(MarkerBar markerBar) { | |
97 | MarkerBarListener listener = lookupListsner(markerBar); | |
98 | if (listener != null) { | |
99 | if (markerBar.getDataModel() != null) | |
100 | markerBar.getDataModel().removeTableModelListener(listener); | |
101 | if (markerBar.getSelectionModel() != null) | |
102 | markerBar.getSelectionModel().removeListSelectionListener(listener); | |
103 | markerBar.removeMouseListener(listener); | |
104 | markerBar.removeMouseMotionListener(listener); | |
105 | markerBar.removeFocusListener(listener); | |
106 | markerBar.removePropertyChangeListener(listener); | |
107 | markerBar.addComponentListener(listener); | |
108 | } | |
109 | ||
110 | } | |
111 | ||
112 | protected MarkerBarListener lookupListsner(MarkerBar markerBar) { | |
113 | MouseMotionListener[] listeners = markerBar.getMouseMotionListeners(); | |
114 | ||
115 | if (listeners != null) { | |
116 | for (int counter = 0; counter < listeners.length; counter++) { | |
117 | if (listeners[counter] instanceof MarkerBarListener) { | |
118 | return (MarkerBarListener) listeners[counter]; | |
119 | } | |
120 | } | |
121 | } | |
122 | return null; | |
123 | } | |
124 | ||
125 | protected MarkerBarListener createListener(MarkerBar markerBar) { | |
126 | return new MarkerBarListener(this, markerBar); | |
127 | } | |
128 | ||
129 | public void paint(Graphics g, JComponent c) { | |
130 | ||
131 | MarkerBar bar = (MarkerBar) c; | |
132 | ||
133 | calculateViewRectAndBaseline(bar); | |
134 | ||
135 | if (bar.isFocusOwner()) | |
136 | drawFocusRect(g, viewRect); | |
137 | ||
138 | BoundedTableModel dataModel = bar.getDataModel(); | |
139 | if (dataModel == null) | |
140 | return; | |
141 | ||
142 | int numAreas = dataModel.getRowCount(); | |
143 | Polygon areas[] = calculateMarkerAreas(bar); | |
144 | ||
145 | ListSelectionModel selectionModel = bar.getSelectionModel(); | |
146 | ||
147 | for (int i = 0; i < numAreas; i++) { | |
148 | boolean isMovable = dataModel.isCellEditable(i, bar.getPositionColumn()); | |
149 | boolean isLeadSelect = (selectionModel == null || selectionModel | |
150 | .isSelectionEmpty()) ? false | |
151 | : selectionModel.getLeadSelectionIndex() == i; | |
152 | if (!isLeadSelect) | |
153 | drawMarker(g, areas[i], false, (Color) dataModel.getValueAt(i, bar | |
154 | .getColorColumn())); | |
155 | } | |
156 | ||
157 | if (selectionModel != null && !selectionModel.isSelectionEmpty()) { | |
158 | int selectedIndex = selectionModel.getLeadSelectionIndex(); | |
159 | boolean isMovable = dataModel.isCellEditable(selectedIndex, bar | |
160 | .getPositionColumn()); | |
161 | ||
162 | if (selectedIndex < numAreas) { | |
163 | drawMarker(g, areas[selectedIndex], /* bar.isFocusOwner() */ | |
164 | true, (Color) dataModel.getValueAt(selectedIndex, bar.getColorColumn())); | |
165 | } else { | |
166 | throw new IllegalStateException( | |
167 | "Selection model inconsistent with data model: " + "element at " | |
168 | + selectedIndex + " selected, but does not exist."); | |
169 | } | |
170 | } | |
171 | ||
172 | } | |
173 | ||
174 | protected void drawFocusRect(Graphics g, Rectangle viewRect) { | |
175 | g.setColor(getFocusColor()); | |
176 | ||
177 | BasicGraphicsUtils.drawDashedRect(g, viewRect.x, viewRect.y, viewRect.width, | |
178 | viewRect.height); | |
179 | } | |
180 | ||
181 | protected void drawMarker(Graphics g, Polygon p, boolean isSelected, Color color) { | |
182 | ||
183 | Color innerColor = color != null ? color : getNormalColor(); | |
184 | // Color selColor = getSelectionColor(); | |
185 | Color selColor = isSelected ? getTextColor() : innerColor; | |
186 | ||
187 | // body | |
188 | g.setColor(innerColor); | |
189 | g.fillPolygon(p); | |
190 | ||
191 | // bick | |
192 | ||
193 | if (isSelected) { | |
194 | int xx[] = new int[3]; | |
195 | int yy[] = new int[3]; | |
196 | ||
197 | xx[0] = p.xpoints[0]; | |
198 | xx[1] = p.xpoints[1]; | |
199 | xx[2] = p.xpoints[p.npoints - 1]; | |
200 | yy[0] = p.ypoints[0]; | |
201 | yy[1] = p.ypoints[1]; | |
202 | yy[2] = p.ypoints[p.npoints - 1]; | |
203 | ||
204 | Polygon bickP = new Polygon(xx, yy, 3); | |
205 | g.setColor(selColor); | |
206 | g.fillPolygon(bickP); | |
207 | g.drawPolygon(bickP); | |
208 | } | |
209 | ||
210 | // outline | |
211 | g.setColor(getTextColor()); | |
212 | g.drawPolygon(p); | |
213 | ||
214 | } | |
215 | ||
216 | public Dimension getMinimumSize(JComponent c) { | |
217 | MarkerBar bar = (MarkerBar) c; | |
218 | Insets ins = bar.getInsets(); | |
219 | ||
220 | if (bar.getOrientation() == SwingConstants.HORIZONTAL) | |
221 | return new Dimension(MARKER_BODY_WIDTH + ins.left + ins.right + 1, | |
222 | MARKER_BODY_HEIGHT + ins.top + ins.bottom + 1); | |
223 | else | |
224 | return new Dimension(MARKER_BODY_HEIGHT + ins.top + ins.bottom + 1, | |
225 | MARKER_BODY_WIDTH + ins.left + ins.right + 1); | |
226 | } | |
227 | ||
228 | public Dimension getPreferredSize(JComponent c) { | |
229 | ||
230 | MarkerBar bar = (MarkerBar) c; | |
231 | BoundedTableModel dataModel = bar.getDataModel(); | |
232 | Insets ins = bar.getInsets(); | |
233 | ||
234 | int r; | |
235 | if (dataModel == null) | |
236 | r = 1; | |
237 | else | |
238 | r = dataModel.getUpperBound() - dataModel.getLowerBound(); | |
239 | ||
240 | if (bar.getOrientation() == SwingConstants.HORIZONTAL) | |
241 | return new Dimension((r) * 2 + MARKER_BODY_WIDTH + ins.left + ins.right + 1, | |
242 | MARKER_BODY_HEIGHT + MARKER_BICK_HEIGHT + ins.top + ins.bottom + 1); | |
243 | else | |
244 | return new Dimension(MARKER_BODY_HEIGHT + MARKER_BICK_HEIGHT + ins.top | |
245 | + ins.bottom + 1, (r) * 2 + MARKER_BODY_WIDTH + ins.left + ins.right | |
246 | + 1); | |
247 | ||
248 | } | |
249 | ||
250 | public Color getFocusColor() { | |
251 | return focusColor; | |
252 | } | |
253 | ||
254 | public Color getShadowColor() { | |
255 | return shadowColor; | |
256 | } | |
257 | ||
258 | public Color getNormalColor() { | |
259 | return normalColor; | |
260 | } | |
261 | ||
262 | public Color getSelectionColor() { | |
263 | return selectionColor; | |
264 | } | |
265 | ||
266 | public Color getDisabledColor() { | |
267 | return disabledColor; | |
268 | } | |
269 | ||
270 | public Color getTextColor() { | |
271 | return textColor; | |
272 | } | |
273 | ||
274 | } |
0 | package com.michaelbaranov.microba.marker.ui.metal; | |
1 | ||
2 | import java.awt.Graphics; | |
3 | import java.awt.Rectangle; | |
4 | ||
5 | import javax.swing.JComponent; | |
6 | import javax.swing.plaf.ComponentUI; | |
7 | ||
8 | import com.michaelbaranov.microba.marker.ui.basic.BasicMarkerBarUI; | |
9 | ||
10 | public class MetalMarkerBarUI extends BasicMarkerBarUI { | |
11 | ||
12 | public static ComponentUI createUI(JComponent c) { | |
13 | return new MetalMarkerBarUI(); | |
14 | } | |
15 | ||
16 | protected void drawFocusRect(Graphics g, Rectangle viewRect) { | |
17 | g.setColor(getFocusColor()); | |
18 | ||
19 | g.drawRect(viewRect.x, viewRect.y, viewRect.width - 1, viewRect.height - 1); | |
20 | } | |
21 | ||
22 | } |
0 | package com.michaelbaranov.microba.marker.ui.motif; | |
1 | ||
2 | import java.awt.Graphics; | |
3 | import java.awt.Rectangle; | |
4 | ||
5 | import javax.swing.JComponent; | |
6 | import javax.swing.plaf.ComponentUI; | |
7 | ||
8 | import com.michaelbaranov.microba.marker.ui.basic.BasicMarkerBarUI; | |
9 | ||
10 | public class MotifMarkerBarUI extends BasicMarkerBarUI { | |
11 | ||
12 | public static ComponentUI createUI(JComponent c) { | |
13 | return new MotifMarkerBarUI(); | |
14 | } | |
15 | ||
16 | protected void drawFocusRect(Graphics g, Rectangle viewRect) { | |
17 | // focus painting is handled by the border | |
18 | } | |
19 | ||
20 | } |
0 | package com.michaelbaranov.microba.marker.ui.windows; | |
1 | ||
2 | import java.awt.Graphics; | |
3 | import java.awt.Rectangle; | |
4 | ||
5 | import javax.swing.JComponent; | |
6 | import javax.swing.UIManager; | |
7 | import javax.swing.plaf.ComponentUI; | |
8 | import javax.swing.plaf.basic.BasicGraphicsUtils; | |
9 | ||
10 | import com.michaelbaranov.microba.marker.MarkerBar; | |
11 | import com.michaelbaranov.microba.marker.ui.basic.BasicMarkerBarUI; | |
12 | ||
13 | public class WindowsMarkerBarUI extends BasicMarkerBarUI { | |
14 | ||
15 | private int dashedRectGapX; | |
16 | ||
17 | private int dashedRectGapY; | |
18 | ||
19 | private int dashedRectGapWidth; | |
20 | ||
21 | private int dashedRectGapHeight; | |
22 | ||
23 | private boolean defaults_initialized; | |
24 | ||
25 | public static ComponentUI createUI(JComponent c) { | |
26 | return new WindowsMarkerBarUI(); | |
27 | } | |
28 | ||
29 | protected void drawFocusRect(Graphics g, Rectangle viewRect) { | |
30 | g.setColor(getFocusColor()); | |
31 | ||
32 | g.setColor(getFocusColor()); | |
33 | // BasicGraphicsUtils.drawDashedRect(g, dashedRectGapX, dashedRectGapY, | |
34 | // viewRect.width - dashedRectGapWidth, viewRect.height | |
35 | // - dashedRectGapHeight); | |
36 | BasicGraphicsUtils.drawDashedRect(g, viewRect.x, viewRect.y, viewRect.width - 1, | |
37 | viewRect.height - 1); | |
38 | } | |
39 | ||
40 | protected void installDefaults(MarkerBar b) { | |
41 | super.installDefaults(b); | |
42 | // if(!defaults_initialized) { | |
43 | dashedRectGapX = UIManager.getInt("Button.dashedRectGapX"); | |
44 | dashedRectGapY = UIManager.getInt("Button.dashedRectGapY"); | |
45 | dashedRectGapWidth = UIManager.getInt("Button.dashedRectGapWidth"); | |
46 | dashedRectGapHeight = UIManager.getInt("Button.dashedRectGapHeight"); | |
47 | // focusColor = UIManager.getColor(pp + "focus"); | |
48 | defaults_initialized = true; | |
49 | } | |
50 | } |