--- a/components/visual-panels/core/src/java/util/com/oracle/solaris/vp/util/swing/time/TimeSpinnerEditor.java Tue Dec 16 05:53:51 2014 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,511 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
- */
-
-package com.oracle.solaris.vp.util.swing.time;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.text.*;
-import java.util.*;
-import javax.swing.*;
-import javax.swing.event.*;
-import javax.swing.text.DefaultCaret;
-import com.oracle.solaris.vp.util.swing.GUIUtil;
-
-@SuppressWarnings({"serial"})
-public class TimeSpinnerEditor extends JTextField {
- //
- // Instance data
- //
-
- private DateFormat format;
- private TimeSpinnerModel model;
- private Date time;
- private boolean ignoreCaretEvents;
- private FieldPosition selected;
-
- // Map Format.Fields to Calendar fields
- // These are all only fields that TimeSpinnerModel supports.
- private Map<Format.Field, Integer> fieldMap;
- {
- fieldMap = new HashMap<Format.Field, Integer>();
- fieldMap.put(DateFormat.Field.AM_PM, Calendar.AM_PM);
- fieldMap.put(DateFormat.Field.DAY_OF_MONTH, Calendar.DAY_OF_MONTH);
- fieldMap.put(DateFormat.Field.DAY_OF_WEEK, Calendar.DAY_OF_WEEK);
- fieldMap.put(DateFormat.Field.DAY_OF_YEAR, Calendar.DAY_OF_YEAR);
- fieldMap.put(DateFormat.Field.ERA, Calendar.ERA);
- fieldMap.put(DateFormat.Field.HOUR0, Calendar.HOUR);
- fieldMap.put(DateFormat.Field.HOUR1, Calendar.HOUR);
- fieldMap.put(DateFormat.Field.HOUR_OF_DAY0, Calendar.HOUR_OF_DAY);
- fieldMap.put(DateFormat.Field.HOUR_OF_DAY1, Calendar.HOUR_OF_DAY);
- fieldMap.put(DateFormat.Field.MILLISECOND, Calendar.MILLISECOND);
- fieldMap.put(DateFormat.Field.MINUTE, Calendar.MINUTE);
- fieldMap.put(DateFormat.Field.MONTH, Calendar.MONTH);
- fieldMap.put(DateFormat.Field.SECOND, Calendar.SECOND);
- fieldMap.put(DateFormat.Field.TIME_ZONE, Calendar.ZONE_OFFSET);
- fieldMap.put(DateFormat.Field.WEEK_OF_MONTH, Calendar.WEEK_OF_MONTH);
- fieldMap.put(DateFormat.Field.WEEK_OF_YEAR, Calendar.WEEK_OF_YEAR);
- fieldMap.put(DateFormat.Field.YEAR, Calendar.YEAR);
- };
-
- private FieldPosition[] positions;
- {
- Set<Format.Field> fields = fieldMap.keySet();
- positions = new FieldPosition[fields.size()];
- int i = 0;
- for (Format.Field field : fields) {
- positions[i++] = new FieldPosition(field);
- }
- }
-
- private Comparator<FieldPosition> posComparator =
- new Comparator<FieldPosition>() {
- @Override
- public int compare(FieldPosition o1, FieldPosition o2) {
- int begin1 = o1.getBeginIndex();
- int begin2 = o2.getBeginIndex();
-
- if (begin1 < begin2) {
- return -1;
- } else if (begin1 > begin2) {
- return 1;
- }
-
- int end1 = o1.getEndIndex();
- int end2 = o2.getEndIndex();
-
- if (end1 < end2) {
- return -1;
- } else if (end1 > end2) {
- return 1;
- }
-
- return 0;
- }
- };
-
- //
- // Constructors
- //
-
- public TimeSpinnerEditor(TimeSpinnerModel model, DateFormat format) {
- this.model = model;
- this.format = format;
-
- setEditable(false);
-
- model.addChangeListener(
- new ChangeListener() {
- @Override
- public void stateChanged(ChangeEvent e) {
- updateFromModel();
- }
- });
-
- updateFromModel();
-
- model.getTimeSelectionModel().addChangeListener(
- new ChangeListener() {
- @Override
- public void stateChanged(ChangeEvent e) {
- selectionChanged();
- }
- });
-
- addCaretListener(
- new CaretListener() {
- @Override
- public void caretUpdate(CaretEvent e) {
- if (!ignoreCaretEvents) {
- EventQueue.invokeLater(
- new Runnable() {
- @Override
- public void run() {
- selectFieldAtCaret();
- }
- });
- }
- }
- });
-
- // Select first field
- selectFieldAtCaret();
-
- addFocusListener(
- new FocusListener() {
- @Override
- public void focusGained(FocusEvent e) {
- if (getModel().getTimeSelectionModel().getSelectedField() ==
- -1) {
- selectFieldAtCaret();
- }
- }
-
- @Override
- public void focusLost(FocusEvent e) {
- if (!e.isTemporary()) {
- getModel().getTimeSelectionModel().setSelectedField(-1);
- }
- }
- });
-
- KeyStroke right = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0);
- Action focusNextFieldAction =
- new AbstractAction() {
- @Override
- public void actionPerformed(ActionEvent e) {
- focusNextField();
- }
- };
- GUIUtil.installKeyBinding(this, JComponent.WHEN_FOCUSED,
- "next field", focusNextFieldAction, right);
-
- KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
- GUIUtil.removeFocusTraversalKeys(this, true, tab);
- Action focusNextFieldOrNextComponentAction =
- new AbstractAction() {
- @Override
- public void actionPerformed(ActionEvent e) {
- focusNextFieldOrNextComponent();
- }
- };
- GUIUtil.installKeyBinding(this, JComponent.WHEN_FOCUSED,
- "next field or next focus", focusNextFieldOrNextComponentAction,
- tab);
-
- KeyStroke left = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
- Action focusPreviousFieldAction =
- new AbstractAction() {
- @Override
- public void actionPerformed(ActionEvent e) {
- focusPreviousField();
- }
- };
- GUIUtil.installKeyBinding(this, JComponent.WHEN_FOCUSED,
- "previous field", focusPreviousFieldAction, left);
-
- KeyStroke shiftTab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
- InputEvent.SHIFT_DOWN_MASK);
- GUIUtil.removeFocusTraversalKeys(this, false, shiftTab);
- Action focusPreviousFieldOrPreviousComponentAction =
- new AbstractAction() {
- @Override
- public void actionPerformed(ActionEvent e) {
- focusPreviousFieldOrPreviousComponent();
- }
- };
- GUIUtil.installKeyBinding(this, JComponent.WHEN_FOCUSED,
- "previous field or previous focus",
- focusPreviousFieldOrPreviousComponentAction, shiftTab);
- }
-
- public TimeSpinnerEditor(JSpinner spinner) {
- this((TimeSpinnerModel)spinner.getModel(), createDateFormat(spinner));
- }
-
- //
- // Component methods
- //
-
- /*
- * Every once in a while a bug comes along that can only be worked around by
- * implementing a truly vile and repulsive hack. This hack violates several
- * tenets of good programming practice.
- *
- * My hand was forced, however, by the extremely limited implementation of
- * the javax.swing.text.DefaultCaret class. This class refuses to even
- * consider the possibility that a developer or user might not want every
- * single selection in a JTextComponent (in this class, numerous and
- * frequently changed) to be automatically copied to the system selection.
- * It further complicates the matter with excessively restrictive access
- * control on several methods which might otherwise be easily overridden to
- * change this default behavior.
- *
- * You might think that overriding copy() and paste() would be sufficient,
- * but those methods don't apply to the system selection.
- *
- * A proper fix might implement a Caret class to do exactly what
- * DefaultCaret does, minus the updateSystemSelection method. However,
- * DefaultCaret.java is ~2000 lines long, and our implementation would need
- * to keep pace with future developements in DefaultCaret.
- *
- * So we are left with this. Examining a stack trace to see who's calling
- * us is nauseatingly hideous, but surprisingly succinct. Throwing a
- * SecurityException whose handling we can know about only by examining the
- * DefaultCaret source code (not the API) is the cherry on the icing.
- */
- @Override
- public Toolkit getToolkit() {
- StackTraceElement[] trace = Thread.currentThread().getStackTrace();
- if (trace.length >= 4) {
- String className = DefaultCaret.class.getName();
- if (trace[2].getClassName().equals(className) &&
- trace[2].getMethodName().equals("getSystemSelection") &&
- trace[3].getClassName().equals(className) &&
- trace[3].getMethodName().equals("updateSystemSelection")) {
- throw new SecurityException();
- }
- }
- return super.getToolkit();
- }
-
- //
- // TimeSpinnerEditor methods
- //
-
- /**
- * Focusses/highlights the next field in the {@code DateFormat}, if the last
- * field is not already focussed.
- *
- * @return {@code true} if the next field was successfully focussed,
- * {@code false} if there is no next field
- */
- public boolean focusNextField() {
- for (int i = 0; i < positions.length - 1; i++) {
- if (positions[i] == selected) {
- int calField = fieldPositionToCalendarField(positions[i + 1]);
- model.getTimeSelectionModel().setSelectedField(calField);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Focusses/highlights the previous field in the {@code DateFormat}, if the
- * first field is not already focussed.
- *
- * @return {@code true} if the previous field was successfully
- * focussed, {@code false} if there is no previous field
- */
- public boolean focusPreviousField() {
- for (int i = positions.length - 1; i > 0; i--) {
- if (positions[i] == selected) {
- for (i--; i >= 0; i--) {
- // Does this field actually appear in this format?
- if (positions[i].getBeginIndex() !=
- positions[i].getEndIndex()) {
-
- int calField = fieldPositionToCalendarField(
- positions[i]);
- model.getTimeSelectionModel().setSelectedField(
- calField);
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Gets the {@link TimeSpinnerModel} used by this {@code TimeSpinnerEditor}.
- */
- public TimeSpinnerModel getModel() {
- return model;
- }
-
- /**
- * Gets the last time that was set in this field.
- */
- public Date getTime() {
- return time;
- }
-
- /**
- * Sets the time in this field.
- */
- public void setTime(Date time) {
- this.time = time;
-
- // Don't react to caret events -- setSelectedField will set the
- // selection manually (and then reset this value)
- ignoreCaretEvents = true;
-
- setText(format.format(time));
-
- // Set begin/end in each FieldPosition
- StringBuffer buffer = new StringBuffer();
- for (FieldPosition position : positions) {
- buffer.setLength(0);
- format.format(time, buffer, position);
- }
-
- // Sort FieldPositions in the order in which they appear
- Arrays.sort(positions, posComparator);
-
- // Restore selection, if possible
- setSelectedField(this.selected);
- }
-
- //
- // Private methods
- //
-
- private int distance(int begin, int end, int pos) {
- assert begin <= end;
- if (pos <= begin) {
- return begin - pos;
- }
- return pos - end;
- }
-
- private void focusNextFieldOrNextComponent() {
- if (!focusNextField() && hasFocus()) {
- transferFocus();
- }
- }
-
- private void focusPreviousFieldOrPreviousComponent() {
- if (!focusPreviousField() && hasFocus()) {
- transferFocusBackward();
- }
- }
-
- private void selectFieldAtCaret() {
- int caret = getSelectionStart();
-
- // Find FieldPosition closest to caret
- FieldPosition position = null;
- int minDistance = 0;
- for (int i = positions.length - 1; i >= 0; i--) {
- int begin = positions[i].getBeginIndex();
- int end = positions[i].getEndIndex();
-
- // Is this field used in this format?
- if (begin != end) {
- int distance = distance(begin, end, caret);
- if (position == null || distance < minDistance) {
- position = positions[i];
- minDistance = distance;
- if (distance <= 0) {
- break;
- }
- } else {
- break;
- }
- }
- }
-
- int calField = fieldPositionToCalendarField(position);
- model.getTimeSelectionModel().setSelectedField(calField);
- }
-
- private FieldPosition calendarFieldToFieldPosition(int calField) {
- if (calField != -1) {
- for (int i = positions.length - 1; i >= 0; i--) {
- // Is this field used in this format?
- if (positions[i].getBeginIndex() != positions[i].getEndIndex())
- {
- Format.Field fmtField = positions[i].getFieldAttribute();
- if (fieldMap.get(fmtField) == calField) {
- return positions[i];
- }
- }
- }
- }
- return null;
- }
-
- private int fieldPositionToCalendarField(FieldPosition position) {
- Format.Field fmtField = position.getFieldAttribute();
- return fieldMap.get(fmtField);
- }
-
- private void selectionChanged() {
- int calendarField = model.getTimeSelectionModel().getSelectedField();
- FieldPosition position = calendarFieldToFieldPosition(calendarField);
- setSelectedField(position);
- }
-
- private void setSelectedField(FieldPosition selected) {
- this.selected = selected;
-
- int newSelStart = 0;
- int newSelEnd = 0;
-
- if (selected != null) {
- newSelStart = selected.getBeginIndex();
- newSelEnd = selected.getEndIndex();
- }
-
- try {
- int selStart = getSelectionStart();
- if (selStart != newSelStart) {
- // Don't recurse
- ignoreCaretEvents = true;
- setCaretPosition(newSelStart);
- }
-
- int selEnd = getSelectionEnd();
- if (selEnd != newSelEnd) {
- // Don't recurse
- ignoreCaretEvents = true;
- moveCaretPosition(newSelEnd);
- }
-
- if (ignoreCaretEvents) {
- EventQueue.invokeLater(
- new Runnable() {
- @Override
- public void run() {
- ignoreCaretEvents = false;
- }
- });
- }
-
- // Thrown by {set,move}CaretPosition -- see 10392
- } catch (IllegalArgumentException ignore) {
- }
- }
-
- private void updateFromModel() {
- setTime(model.getTimeModel().getCalendar().getTime());
- }
-
- //
- // Static methods
- //
-
- protected static DateFormat createDateFormat(JSpinner spinner) {
- Locale locale = spinner.getLocale();
-
- DateFormat format = DateFormat.getTimeInstance(DateFormat.MEDIUM,
- locale == null ? Locale.getDefault() : locale);
-
- if (format instanceof SimpleDateFormat) {
- padUnits((SimpleDateFormat)format);
- }
-
- return format;
- }
-
- protected static void padUnits(SimpleDateFormat format) {
- String pattern = format.toPattern().replaceAll(
- "\\b([yMwWDdFHkKhmsS])\\b", "$1$1");
-
- format.applyPattern(pattern);
- }
-}