Ticket #2281 (closed bug: obsolete)

Opened 11 years ago

Last modified 9 years ago

consistent-time-format -- Times should be consistent in the application

Reported by: deyan Owned by: stefan
Priority: major Milestone: X3
Component: uncategorized Version: 2.0
Keywords: Cc: stefan
Category: unknown Effort:
Importance: Ticket_group:
Estimated Number of Hours: 0 Add Hours to Ticket: 0
Billable?: yes Total Hours: 0
Analysis_owners: tsachev Design_owners: stefan
Imp._owners: stefan Test_owners:
Analysis_reviewers: stefan Changelog: Changelog
Design_reviewers: Imp._reviewers: pap, kyli
Test_reviewers: Analysis_score: 3.5
Design_score: 0 Imp._score: 4
Test_score: 0

Description (last modified by stefan) (diff)

Make the time format in the application in the following format: MM:SS:sss. This applies to:

  • Timeline head
  • Timeline HUD
  • Timeline tick label

When the user edits a whole field, the value he has entered should be converted to the format from milliseconds

Attachments

2281_specific_place.patch (156.3 KB) - added by stefan 10 years ago.

Change History

comment:1 Changed 11 years ago by stefan

  • Category set to unknown
  • Status changed from new to s1b_analysis_finished
  • Description modified (diff)
  • Analysis_score set to 0
  • Test_score set to 0
  • Design_score set to 0
  • Imp._score set to 0

comment:2 Changed 11 years ago by stefan

  • Status changed from s1b_analysis_finished to s2a_design_started

comment:3 Changed 11 years ago by stefan

  • Status changed from s2a_design_started to s3b_implementation_finished

Basically, this task is contains three minor tweaks:

  • change in the time format in org.sophie2.main.func.timelines.hud.HeadSection in the compute() method:

SimpleDateFormat sdf = new SimpleDateFormat("mm:ss:SSS");

  • method createSpinnerEditor was added to the BoundSpinner class which, by default returns null, in which case nothing happens, i.e. the behavior of the BoundSpinner isn't changed. When this method is overridden by some child of BoundSpinner}}, it returns {{{JPanel (in our case wrapper of JFormattedTextField) that is used as a editor in the swing's JSpinner API.
    • code of significance for our editor (the inner class of BaseTimelinesSpinner), as described above:
      private String formatTime(int millis) {
      			int min = millis / 1000 / 60;
      			int sec = millis / 1000 % 60;
      			int mil = millis % 1000;
      			return String.format(FORMAT, min, sec, mil);
      		}
      
      		private int extractTime(String time) {
      			String[] tokens = time.split(":");
      			assert (tokens.length == 3);
      			int min = Integer.parseInt(tokens[0]);
      			int sec = Integer.parseInt(tokens[1]);
      			int mil = Integer.parseInt(tokens[2]);
      			return mil + (sec * 1000) + (min * 60 * 1000);
      		}
      
      		public void stateChanged(ChangeEvent e) {
      			JSpinner spinnerSource = (JSpinner) (e.getSource());
      			int val = (Integer) spinnerSource.getValue();
      			getTextField().setValue(formatTime(val));
      
      		}
      
      		public void propertyChange(PropertyChangeEvent evt) {
      			if (this.spinner == null) {
      				return;
      			}
      
      			Object source = evt.getSource();
      			String name = evt.getPropertyName();
      			if ((source instanceof JFormattedTextField) && "value".equals(name)) {
      				int lastValue = (Integer) this.spinner.getValue();
      
      				try {
      					String textValue = (String) getTextField().getValue();
      					this.spinner.setValue(extractTime(textValue));
      				} catch (IllegalArgumentException iae) {
      					try {
      						((JFormattedTextField) source).setValue(lastValue);
      					} catch (IllegalArgumentException iae2) {
      						// TODO do nothing if field invalid
      					}
      				}
      			}
      
      		}
      	}
      
  • change the display of the Timeline's play head. This cannot be implemented because this value is hard-coded property in the JSpinner's API. There is only possibility (the property is called Spinner.showValue) to choose whether it is showed or not.

comment:4 Changed 11 years ago by stefan

  • Design_owners set to stefan
  • Imp._owners set to stefan

comment:5 Changed 11 years ago by meddle

  • Cc stefan added
  • Status changed from s3b_implementation_finished to new

I'm failing the ticket because when you enter in the spinner editable field a number that is no in the format d:d:d, an assertation error is thrown.

Another thing, when you pick a bug/tweak ticket you review it's analysis to see if you understand what is the problem. You can fail it if you don't understand it. When you pass it you must score it and write yourself as an analysis reviewer.

comment:6 Changed 11 years ago by stefan

  • Status changed from new to s1b_analysis_finished
  • Analysis_reviewers set to stefan
  • Description modified (diff)

the analysis is fine, except the fact that there can be nothing done to change the value format of the timeline's playing head. "Timeline tick label" should also be added in the conditions of the bug.

comment:7 Changed 11 years ago by stefan

  • Status changed from s1b_analysis_finished to s2a_design_started

comment:8 Changed 11 years ago by stefan

  • Analysis_score changed from 0 to 3.5

comment:9 Changed 11 years ago by stefan

  • Status changed from s2a_design_started to s3b_implementation_finished

Basically, this task is contains three minor tweaks:

  • change in the time format in org.sophie2.main.func.timelines.hud.HeadSection in the compute() method:
    SimpleDateFormat sdf = new SimpleDateFormat("mm:ss:SSS");
    
  • method createSpinnerEditor was added to the BoundSpinner class which, by default returns null, in which case nothing happens, i.e. the behavior of the BoundSpinner isn't changed. When this method is overridden by some child of BoundSpinner}}, it returns JPanel (in our case wrapper of JFormattedTextField) that is used as a editor in the swing's JSpinner API.
    • code of significance for our editor (the inner class of BaseTimelinesSpinner), as described above:
      private int extractTime(String time) {
      			String[] tokens = time.split(":");
      			assert (tokens.length == 3);
      			int min = Integer.parseInt(tokens[0]);
      			int sec = Integer.parseInt(tokens[1]);
      			int mil = Integer.parseInt(tokens[2]);
      			return mil + (sec * 1000) + (min * 60 * 1000);
      		}
      
      		public void stateChanged(ChangeEvent e) {
      			JSpinner spinnerSource = (JSpinner) (e.getSource());
      			int val = (Integer) spinnerSource.getValue();
      			getTextField().setValue(formatTime(val));
      		}
      		
      		public boolean validate(String field) {
      			String[] tokens = field.split(":");
      			if (!(tokens.length == 3)) {
      				return false;
      			}
      			for (int i = 0; i < tokens.length; i++) {
      				for (int j = 0; j < tokens[i].length(); j++) {
      					if (!Character.isDigit(tokens[i].charAt(j))) {
      						return false;
      					}
      				}
      			}
      			return true;
      		}
      
      		public void propertyChange(PropertyChangeEvent evt) {
      			if (this.spinner == null) {
      				return;
      			}
      
      			Object source = evt.getSource();
      			String name = evt.getPropertyName();
      			if ((source instanceof JFormattedTextField) && "value".equals(name)) {
      				int lastValue = (Integer) this.spinner.getValue();
      
      				try {
      					String textValue = (String) getTextField().getValue();
      					if (!validate(textValue)) {
      						((JFormattedTextField) source).setValue(formatTime(lastValue));
      						return;
      					}
      					this.spinner.setValue(extractTime(textValue));
      				} catch (IllegalArgumentException iae) {
      					try {
      						((JFormattedTextField) source).setValue(formatTime(lastValue));
      					} catch (IllegalArgumentException iae2) {
      						// TODO do nothing if field invalid
      					}
      				}
      			}
      
      		}
      
    • as you can see, a method validate() is added to the inner class, in order to provide validation of the format of the input - xx:xx:xxx. The validation of the real value (number of milliseconds) in the logic of the application is provided by the BoundSpinner.validate method.
  • change the display of the Timeline's play head. This cannot be implemented because this value is hard-coded property in the JSpinner's API. There is only possibility (the property is called Spinner.showValue) to choose whether it is showed or not.

comment:10 Changed 11 years ago by mira

  • Status changed from s3b_implementation_finished to s2c_design_ok
  • Imp._reviewers set to pap
  • I fail the task, because I cannot apply the patch to the trunk

comment:11 Changed 11 years ago by stefan

  • Owner set to stefan
  • Status changed from s2c_design_ok to s3a_implementation_started

comment:12 Changed 11 years ago by stefan

  • Status changed from s3a_implementation_started to s3b_implementation_finished

comment:13 Changed 10 years ago by stefan

The last patch contains SpinnerNumberModel in order to provide capability of using the spinner to change (increase or decrease) the value of minutes, seconds or milliseconds of the spinner, if the caret of the text field is in the minutes, seconds or milliseconds section respectfully. The code of significance:

/**
	 * An implementation of {@link SpinnerNumberModel} class, in order to
	 * provide capability
	 * 
	 * @author stefan
	 * 
	 */
	class BaseTimelineSpinnerModel extends SpinnerNumberModel {

		private static final long serialVersionUID = 5065673918920549095L;

		private int step;

		/**
		 * The default and only constructor.
		 * 
		 * @param step
		 *            The default step in milliseconds - usually
		 *            {@link BaseTimelinesSpinner#STEP_SIZE_MILLIS}
		 */
		public BaseTimelineSpinnerModel(int step) {
			super(Integer.valueOf(0), Integer.valueOf(0), null, Integer
					.valueOf(step));
			this.step = step;
		}

		@Override
		public Object getNextValue() {
			super.setStepSize(this.step * getStepMult());
			return super.getNextValue();
		}

		@Override
		public Object getPreviousValue() {
			super.setStepSize(this.step * getStepMult());
			return super.getPreviousValue();
		}

		/**
		 * Returns the step size.
		 * 
		 * @return Step.
		 */
		public int getStep() {
			return this.step;
		}
	}

Changed 10 years ago by stefan

comment:14 Changed 10 years ago by kyli

  • Status changed from s3b_implementation_finished to s3c_implementation_ok
  • Imp._reviewers changed from pap to pap, kyli
  • Imp._score changed from 0 to 4
  • Changelog set to [wiki:Changelog]

Seems OK to me. I would just like to note, that using auto-formatting in eclipse is not encouraged by meddle :)

4p, merged with trunk at [8686].

comment:15 Changed 9 years ago by meddle

  • Status changed from s3c_implementation_ok to closed
  • Resolution set to obsolete

Closing all the tickets before M Y1

Note: See TracTickets for help on using tickets.