components/visual-panels/core/src/java/vpanels/panel/com/oracle/solaris/vp/panel/swing/timezone/TimeZonePanel.java
changeset 827 0944d8c0158b
child 1410 ca9946e5736c
equal deleted inserted replaced
826:c6aad84d2493 827:0944d8c0158b
       
     1 /*
       
     2  * CDDL HEADER START
       
     3  *
       
     4  * The contents of this file are subject to the terms of the
       
     5  * Common Development and Distribution License (the "License").
       
     6  * You may not use this file except in compliance with the License.
       
     7  *
       
     8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
       
     9  * or http://www.opensolaris.org/os/licensing.
       
    10  * See the License for the specific language governing permissions
       
    11  * and limitations under the License.
       
    12  *
       
    13  * When distributing Covered Code, include this CDDL HEADER in each
       
    14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
       
    15  * If applicable, add the following below this CDDL HEADER, with the
       
    16  * fields enclosed by brackets "[]" replaced with your own identifying
       
    17  * information: Portions Copyright [yyyy] [name of copyright owner]
       
    18  *
       
    19  * CDDL HEADER END
       
    20  */
       
    21 
       
    22 /*
       
    23  * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
       
    24  */
       
    25 
       
    26 package com.oracle.solaris.vp.panel.swing.timezone;
       
    27 
       
    28 import java.awt.*;
       
    29 import java.awt.geom.Point2D;
       
    30 import java.beans.*;
       
    31 import java.text.DateFormat;
       
    32 import java.util.*;
       
    33 import java.util.List;
       
    34 import javax.swing.event.*;
       
    35 import javax.swing.Icon;
       
    36 import com.oracle.solaris.vp.panel.common.model.View;
       
    37 import com.oracle.solaris.vp.panels.time.*;
       
    38 import com.oracle.solaris.vp.util.misc.finder.Finder;
       
    39 import com.oracle.solaris.vp.util.misc.property.MutableProperty;
       
    40 import com.oracle.solaris.vp.util.swing.layout.ColumnLayoutConstraint;
       
    41 import com.oracle.solaris.vp.util.swing.SettingsPanel;
       
    42 import com.oracle.solaris.vp.util.swing.time.*;
       
    43 
       
    44 public class TimeZonePanel extends SettingsPanel
       
    45     implements View<TimeZoneModel> {
       
    46 
       
    47     //
       
    48     // Static data
       
    49     //
       
    50 
       
    51     private static TimeZoneMapper MAPPER = new TimeZoneMapper();
       
    52 
       
    53     private static final Icon BACKGROUND_ICON =
       
    54 	Finder.getIcon("images/world_map-960.png");
       
    55 
       
    56     /**
       
    57      * Gets the {@code TimeZone} that corresponds to the given {@link
       
    58      * TimeZoneInfo}.
       
    59      *
       
    60      * @return	a {@code TimeZone}, or {@code null} if no {@code
       
    61      *		TimeZone} corresponds to the given {@link TimeZoneInfo}
       
    62      */
       
    63     public static TimeZone toTimeZone(TimeZoneInfo info) {
       
    64 	// Unfortunately TimeZone.getTimeZone(String id) gives no error when
       
    65 	// id is not recognized, so use a different approach
       
    66 	String[] ids = TimeZone.getAvailableIDs();
       
    67 	for (String id : ids) {
       
    68 	    if (id.equals(info.getName()) ||
       
    69 		id.equals(info.getAltName())) {
       
    70 		return TimeZone.getTimeZone(id);
       
    71 	    }
       
    72 	}
       
    73 
       
    74 	// XXX We should never return null.  TimeZoneInfo should have a
       
    75 	// int getRawOffset method to ensure we can always create a
       
    76 	// TimeZone.
       
    77 	return null;
       
    78     }
       
    79 
       
    80     //
       
    81     // Instance data
       
    82     //
       
    83 
       
    84     private LocationPanel<TZChoice> locationPanel_;
       
    85     private MutableProperty<TZChoice> locationProp_;
       
    86 
       
    87     private Map<String, TZChoice> nameToTimeZone =
       
    88 	new HashMap<String, TZChoice>();
       
    89     private Map<String, Continent> continentDesc =
       
    90 	new HashMap<String, Continent>();
       
    91     private Map<String, Country> countryDesc = new HashMap<String, Country>();
       
    92     private Map<Integer, Offset> offsetDesc = new HashMap<Integer, Offset>();
       
    93     private Map<Integer, Offset> rawoffsetDesc = new HashMap<Integer, Offset>();
       
    94 
       
    95     //
       
    96     // Inner classes
       
    97     //
       
    98 
       
    99     /*
       
   100      * Need an object to encapsulate a timezone offset.
       
   101      */
       
   102     private class Offset {
       
   103 	int offset_;
       
   104 
       
   105 	public Offset(int offset) {
       
   106 	    offset_ = offset;
       
   107 	}
       
   108 
       
   109 	@Override
       
   110 	public String toString() {
       
   111 	    boolean neg = offset_ < 0;
       
   112 	    int minutes = Math.abs(offset_) / (60 * 1000);
       
   113 	    int hours = minutes / 60;
       
   114 	    minutes = minutes % 60;
       
   115 	    return (String.format("GMT%s%d:%02d",
       
   116 		neg ? "-" : "+", hours, minutes));
       
   117 	}
       
   118     }
       
   119 
       
   120     /*
       
   121      * We could use TimeZoneInfos directly, but instead we use this wrapper
       
   122      * type to cache the associations between each time zone and its various
       
   123      * attributes.
       
   124      */
       
   125     private class TZChoice {
       
   126 	TimeZoneInfo info_;
       
   127 	Country country_;
       
   128 	Continent continent_;
       
   129 	Offset offset_;
       
   130 	Offset rawoffset_;
       
   131 	String label_;
       
   132 	Point2D point_;
       
   133 
       
   134 	public TZChoice(TimeZoneInfo info) {
       
   135 	    TimeZone tz = TimeZone.getTimeZone(info.getName());
       
   136 	    info_ = info;
       
   137 	    String contname = info.getName().split("/")[0];
       
   138 	    continent_ = continentDesc.get(contname);
       
   139 	    country_ = countryDesc.get(info.getCountryCode());
       
   140 	    offset_ = makeOffset(tz.getOffset(System.currentTimeMillis()),
       
   141 		offsetDesc);
       
   142 	    rawoffset_ = makeOffset(tz.getRawOffset(), rawoffsetDesc);
       
   143 
       
   144 	    String alt = info.getAltName();
       
   145 	    String name = alt != null ? alt : info.getName();
       
   146 	    String display = tz.getDisplayName();
       
   147 	    label_ = Finder.getString("timezone.description", name, display);
       
   148 
       
   149 	    Coordinates coords = info.getCoordinates();
       
   150 	    double east = (double)coords.getDegreesE() +
       
   151 		(coords.getDegreesE() > 0 ? 1 : -1) *
       
   152 		((double)coords.getSecondsE() / 60 +
       
   153 		(double)coords.getMinutesE()) / 60;
       
   154 	    double north = (double)coords.getDegreesN() +
       
   155 		(coords.getDegreesN() > 0 ? 1 : -1) *
       
   156 		((double)coords.getSecondsN() / 60 +
       
   157 		(double)coords.getMinutesN()) / 60;
       
   158 	    east = 180 + east;
       
   159 	    north = 90 - north;
       
   160 	    point_ = new Point2D.Double(east / 360, north / 180);
       
   161 	}
       
   162 
       
   163 	private Offset makeOffset(int offset, Map<Integer, Offset>map) {
       
   164 	    Offset result = map.get(offset);
       
   165 	    if (result == null)
       
   166 		map.put(offset, result = new Offset(offset));
       
   167 	    return (result);
       
   168 	}
       
   169 
       
   170 	public TimeZoneInfo getInfo() {
       
   171 	    return (info_);
       
   172 	}
       
   173     }
       
   174 
       
   175     private static class TimeZoneMapper implements LocationMapper<TZChoice> {
       
   176 	@Override
       
   177 	public Point2D map(TZChoice choice) {
       
   178 	    return choice.point_;
       
   179 	}
       
   180 
       
   181 	@Override
       
   182 	public String getLabel(TZChoice choice) {
       
   183 	    return choice.label_;
       
   184 	}
       
   185     }
       
   186 
       
   187     private class TZOffsetCriteria extends LocationCriteria<TZChoice, Offset>
       
   188 	implements Comparator<Offset> {
       
   189 
       
   190 	public String getLabel() {
       
   191 	    return Finder.getString("timezone.label.offset");
       
   192 	}
       
   193 
       
   194 	public String getUnselectedText() {
       
   195 	    return Finder.getString("timezone.offset.select");
       
   196 	}
       
   197 
       
   198 	@Override
       
   199 	public Comparator<Offset> getComparator() {
       
   200 	    return this;
       
   201 	}
       
   202 
       
   203 	public Offset toCriterion(TZChoice choice) {
       
   204 	    return choice.offset_;
       
   205 	}
       
   206 
       
   207 	public String toDescription(Offset criterion) {
       
   208 	    return criterion.toString();
       
   209 	}
       
   210 
       
   211 	/*
       
   212 	 * Comparator methods
       
   213 	 */
       
   214 
       
   215 	public int compare(Offset o1, Offset o2) {
       
   216 	    return (o1.offset_ - o2.offset_);
       
   217 	}
       
   218     }
       
   219 
       
   220     private class TZContinentCriteria
       
   221 	extends LocationCriteria<TZChoice, Continent> {
       
   222 
       
   223 	public String getLabel() {
       
   224 	    return Finder.getString("timezone.label.continent");
       
   225 	}
       
   226 
       
   227 	public String getUnselectedText() {
       
   228 	    return Finder.getString("timezone.continent.select");
       
   229 	}
       
   230 
       
   231 	public Continent toCriterion(TZChoice choice) {
       
   232 	    return (choice.continent_);
       
   233 	}
       
   234 
       
   235 	public String toDescription(Continent criterion) {
       
   236 	    return criterion.getDescription();
       
   237 	}
       
   238     }
       
   239 
       
   240     private class TZCountryCriteria
       
   241 	extends LocationCriteria<TZChoice, Country> {
       
   242 
       
   243 	public String getLabel() {
       
   244 	    return Finder.getString("timezone.label.country");
       
   245 	}
       
   246 
       
   247 	public String getUnselectedText() {
       
   248 	    return Finder.getString("timezone.country.select");
       
   249 	}
       
   250 
       
   251 	public Country toCriterion(TZChoice choice) {
       
   252 	    return (choice.country_);
       
   253 	}
       
   254 
       
   255 	public String toDescription(Country criterion) {
       
   256 	    return criterion.getDescription();
       
   257 	}
       
   258     }
       
   259 
       
   260     private class TZZoneCriteria extends LocationCriteria<TZChoice, TZChoice> {
       
   261 
       
   262 	public String getLabel() {
       
   263 	    return Finder.getString("timezone.label.timezone");
       
   264 	}
       
   265 
       
   266 	public String getUnselectedText() {
       
   267 	    return Finder.getString("timezone.timezone.select");
       
   268 	}
       
   269 
       
   270 	public TZChoice toCriterion(TZChoice choice) {
       
   271 	    return choice;
       
   272 	}
       
   273 
       
   274 	public String toDescription(TZChoice criterion) {
       
   275 	    return (MAPPER.getLabel(criterion));
       
   276 	}
       
   277     }
       
   278 
       
   279     //
       
   280     // Constructors
       
   281     //
       
   282 
       
   283     public TimeZonePanel(TimeMXBean bean, final SimpleTimeModel model) {
       
   284 
       
   285 	getHelpField().setText(Finder.getString("timezone.desc"));
       
   286 
       
   287 	// Remove the spacing between the help and content panes
       
   288 	ColumnLayoutConstraint constraint =
       
   289 	    getLayout().getConstraint(getContentPane());
       
   290 	constraint.setGap(0);
       
   291 
       
   292 	/*
       
   293 	 * Read metadata from MBean.  Ideally would be read from the model.
       
   294 	 */
       
   295 	List<TimeZoneInfo> infoSet = bean.gettimeZones();
       
   296 	List<Continent> continents = bean.getcontinents();
       
   297 	List<Country> countries = bean.getcountries();
       
   298 
       
   299 	for (Continent c : continents)
       
   300 	    continentDesc.put(c.getName(), c);
       
   301 
       
   302 	for (Country c : countries)
       
   303 	    countryDesc.put(c.getCode(), c);
       
   304 
       
   305 	List<TZChoice> choices = new LinkedList<TZChoice>();
       
   306 	for (TimeZoneInfo zone : infoSet) {
       
   307 	    TZChoice choice = new TZChoice(zone);
       
   308 	    if (choice.continent_ == null || choice.country_ == null)
       
   309 		continue;
       
   310 	    choices.add(choice);
       
   311 	    nameToTimeZone.put(zone.getName(), choice);
       
   312 	    if (zone.getAltName() != null)
       
   313 		nameToTimeZone.put(zone.getAltName(), choice);
       
   314 	}
       
   315 
       
   316 	List<LocationCriteria<TZChoice, ?>> criteria =
       
   317 	    new LinkedList<LocationCriteria<TZChoice, ?>>();
       
   318 	criteria.add(new TZContinentCriteria());
       
   319 	criteria.add(new TZCountryCriteria());
       
   320 	// criteria.add(new TZOffsetCriteria());  // *instead* of the other two
       
   321 
       
   322 	locationPanel_ = new LocationPanel<TZChoice>(BACKGROUND_ICON,
       
   323 	    new Dimension(400, 200), MAPPER);
       
   324 	locationProp_ = locationPanel_.getLocationProperty();
       
   325 	getChangeableAggregator().addChangeables(locationProp_);
       
   326 	locationPanel_.setCriteria(criteria, new TZZoneCriteria(), choices,
       
   327 	    model != null ? Finder.getString("timezone.label.time") : null);
       
   328 
       
   329 	if (model != null) {
       
   330 	    /*
       
   331 	     * Add listener that updates SimpleTimeModel with selected
       
   332 	     * time zone
       
   333 	     */
       
   334 	    locationProp_.addChangeListener(
       
   335 		new ChangeListener() {
       
   336 		    @Override
       
   337 		    public void stateChanged(ChangeEvent e) {
       
   338 			TZChoice choice = locationProp_.getValue();
       
   339 			if (choice != null) {
       
   340 			    TimeZone zone = toTimeZone(choice.getInfo());
       
   341 			    model.setTimeZone(zone);
       
   342 			}
       
   343 		    }
       
   344 		});
       
   345 
       
   346 	    /*
       
   347 	     * And add a listener to the model that updates the auxiliary
       
   348 	     * field of the location panel with the current time.
       
   349 	     */
       
   350 	    final DateFormat format = DateFormat.getDateTimeInstance(
       
   351 		DateFormat.SHORT, DateFormat.SHORT);
       
   352 	    format.setCalendar(model.getModelCalendar());
       
   353 
       
   354 	    PropertyChangeListener timeLabelListener =
       
   355 		new PropertyChangeListener() {
       
   356 		    @Override
       
   357 		    public void propertyChange(PropertyChangeEvent e) {
       
   358 			locationPanel_.getAuxField().setText(format.format(
       
   359 			    model.getCalendar().getTime()));
       
   360 		    }
       
   361 		};
       
   362 	    model.addPropertyChangeListener(TimeModel.PROPERTY_TIME,
       
   363 		timeLabelListener);
       
   364 
       
   365 	    // Initialize
       
   366 	    timeLabelListener.propertyChange(null);
       
   367 	}
       
   368 
       
   369 	getContentPane().add(locationPanel_);
       
   370 	setContent(locationPanel_, false, false);
       
   371     }
       
   372 
       
   373     //
       
   374     // View methods
       
   375     //
       
   376 
       
   377     public void modelToView(TimeZoneModel model) {
       
   378 	// Sanity check -- the UI should be updated only on the event thread
       
   379 	assert EventQueue.isDispatchThread();
       
   380 
       
   381 	String name = model.getTimeZone();
       
   382 	TZChoice timeZone = nameToTimeZone.get(name);
       
   383 	locationProp_.update(timeZone, false);
       
   384     }
       
   385 
       
   386     public void viewToModel(TimeZoneModel model) {
       
   387 	TZChoice tz = locationProp_.getValue();
       
   388 	model.setTimeZone(tz != null ? tz.getInfo().getName() : null);
       
   389     }
       
   390 }