Forum Stats

  • 3,728,694 Users
  • 2,245,675 Discussions
  • 7,853,706 Comments

Discussions

DateSpinner spins only after an edit

Jörg
Jörg Member Posts: 1,301
edited August 2011 in Swing
Hello,

this spinner beeps when using the arrow keys or clicking the UP or DOWN
button. Only after modifying a cipher, one can spin as expected. Where is the error?
The behaviour is the same under jdk 1.6.26 and 1.7.
import java.awt.*;
import java.util.*;
import javax.swing.*;

public class DateSpinner extends JFrame {
  JSpinner spin;

  public DateSpinner() {
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    setSize(200,100);
    setLayout(null);
    Date date= new Date();
    spin= new JSpinner(new SpinnerDateModel(
				date, date, null, Calendar.DAY_OF_MONTH));
    spin.setEditor(new JSpinner.DateEditor(spin, "dd.MM.yyyy"));
    spin.setBounds(50,20, 80,25);
    add(spin);
    setVisible(true);
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
	new DateSpinner();
      }
    });
  }
}

Best Answer

  • kleopatra-JavaNet
    kleopatra-JavaNet Member Posts: 1,105 Gold Badge
    edited August 2011 Accepted Answer
    The underlying problem is the different precision on start:

    - model returns min with the time fields to the second
    - the text in the field is formatted to the minute (short default) or to the day (custom format)
    - BasicSpinnerUI button increment (didn't dig into the keyboard spinning, probably similar) first commits the current text before actually doing the increment
    - for the very first value, that initial commit fails because to the formatter it appears to be before the min value

    A bit rough, but wouldn't regard it as a bug: none of the collaborators can know what your "real" minimum is except you tell them : -)

    Way out is to set the min/max of the spinner/model to the same precision as the format, something like (using SwingX utils - which aren't entirely safe, as they dont handle DST correctly)
          int spinnerField = Calendar.DAY_OF_MONTH;
          Date date= new Date();
          Calendar calendar = Calendar.getInstance();
          CalendarUtils.startOf(calendar, spinnerField);
          spin= new JSpinner(new SpinnerDateModel(
                                      date, calendar.getTime(), null, spinnerField));
          spin.setEditor(new JSpinner.DateEditor(spin, "dd.MM.yyyy"));
    Cheers
    Jeanette

    Edited by: Kleopatra on Aug 11, 2011 1:14 PM

    added a code snippet

Answers

  • sabre150
    sabre150 Member Posts: 1,405
    edited August 2011
    I don't understand what you mean by "Only after modifying a cipher" since it seems to have no relevance to your problem.

    To me the Javadocs for JSpinner and SpinnerDateModel are ambiguous but I think you need something along the lines of
           Date now = new Date();
            Date thisTimeYesterday = new Date(now.getTime() - 24L * 60 * 60 * 1000);
            JSpinner spin = new JSpinner(new SpinnerDateModel(now, thisTimeYesterday, null, Calendar.DAY_OF_MONTH));
            spin.setEditor(new JSpinner.DateEditor(spin, "dd.MM.yyyy"));
    but I'm not sure what this will do when there is a Leap transition within the last 24 hours.

    Edited by: sabre150 on 11-Aug-2011 01:15

    After some further thought I now think you need
           GregorianCalendar nowCalendar = new GregorianCalendar();
            Date now = nowCalendar.getTime();
            nowCalendar.add(Calendar.DAY_OF_MONTH, -1);
            Date thisTimeYesterday = nowCalendar.getTime();
            final SpinnerDateModel spinnerDateModel = new SpinnerDateModel(now, thisTimeYesterday, null, Calendar.DAY_OF_MONTH);
            JSpinner spin = new JSpinner(spinnerDateModel);
      
    which should cope with the Leap transitions though you would need to test it.
  • Jörg
    Jörg Member Posts: 1,301
    Hello Sabre,
    I don't understand what you mean by "Only after modifying a cipher"
    Example: After launching the programme the cursor is at the beginning of the
    TextField. Remove the first cipher (currently "1") and replace it by "2".
    Now you can immediately use the arrow keys or click the buttons,
    which was not possible without the edit. Of course, the modification
    must be made to a valid, meaning higher date, as today is the lower bound.

    Your code works indeed. I found out that I have to reduce the current date (initial value)
    by at least that amount of hours that the lower bound date becomes yesterday.
    So we can conclude, that the initial and the lower bound value cannot be the same.
    Quite odd.
    I had a look at the bug database, but as it is not functioning properly, I could not access all search results.

    Ah! Just read your edit. Well, basically there is no difference to me in the new code,
    as it also keeps the initial and the lower bound values apart.
    Thanks, anyway.
  • sabre150
    sabre150 Member Posts: 1,405
    Jörg wrote:
    I don't understand what you mean by "Only after modifying a cipher"
    Example: After launching the programme the cursor is at the beginning of the
    TextField. Remove the first cipher (currently "1") and replace it by "2".
    Now I'm even more confused since I still have no idea what you mean by a 'cipher' in this context and I don't see a TextField anywhere. Never mind - you seem to have solved the problem anyway so what the heck.
  • Jörg
    Jörg Member Posts: 1,301
    Sorry for confusing you. The JSpinner consists of a JTextField and two (BasicArrow)buttons, if I remember correctly.
    When I launch the programme at my site it currently displays "11.08.2011". After the edit (using the Delete key) it looks like that "21.08.2011". Is that clearer now?

    And no, the problem is not solved, but presently looks to me like a bug. But number of bug impressions turned out to be my own fault.
  • walterln
    walterln Member Posts: 2,302
    The correct English translation for 'cijfer' (at least that is the Dutch word, but I guess your german? scandinavian? equivalent similar) is 'number'. The common meaning of 'cipher' in English is "a method of transforming a text in order to conceal its meaning" (although it can mean number according to my dictionary as well, it is not the common usage afaik).
  • kleopatra-JavaNet
    kleopatra-JavaNet Member Posts: 1,105 Gold Badge
    edited August 2011 Accepted Answer
    The underlying problem is the different precision on start:

    - model returns min with the time fields to the second
    - the text in the field is formatted to the minute (short default) or to the day (custom format)
    - BasicSpinnerUI button increment (didn't dig into the keyboard spinning, probably similar) first commits the current text before actually doing the increment
    - for the very first value, that initial commit fails because to the formatter it appears to be before the min value

    A bit rough, but wouldn't regard it as a bug: none of the collaborators can know what your "real" minimum is except you tell them : -)

    Way out is to set the min/max of the spinner/model to the same precision as the format, something like (using SwingX utils - which aren't entirely safe, as they dont handle DST correctly)
          int spinnerField = Calendar.DAY_OF_MONTH;
          Date date= new Date();
          Calendar calendar = Calendar.getInstance();
          CalendarUtils.startOf(calendar, spinnerField);
          spin= new JSpinner(new SpinnerDateModel(
                                      date, calendar.getTime(), null, spinnerField));
          spin.setEditor(new JSpinner.DateEditor(spin, "dd.MM.yyyy"));
    Cheers
    Jeanette

    Edited by: Kleopatra on Aug 11, 2011 1:14 PM

    added a code snippet
  • sabre150
    sabre150 Member Posts: 1,405
    edited August 2011
    Jörg wrote:
    Sorry for confusing you. The JSpinner consists of a JTextField and two (BasicArrow)buttons, if I remember correctly.
    When I launch the programme at my site it currently displays "11.08.2011". After the edit (using the Delete key) it looks like that "21.08.2011". Is that clearer now?
    I'm still not clear as to what you consider the problem to be. Are you saying you don't want the value displayed to be editable except though the keyboard arrows keys or the displayed arrows? If so then you just need to disable edit on the DateEditor text field.
           final GregorianCalendar nowCalendar = new GregorianCalendar();
            Date now = nowCalendar.getTime();
            nowCalendar.add(Calendar.DAY_OF_MONTH, -1);
            Date thisTimeYesterday = nowCalendar.getTime();
            final SpinnerDateModel spinnerDateModel = new SpinnerDateModel(now, thisTimeYesterday, null, Calendar.DAY_OF_MONTH);
            final JSpinner spin = new JSpinner(spinnerDateModel);
            final JSpinner.DateEditor dateEditor = new JSpinner.DateEditor(spin, "dd.MM.yyyy");
            dateEditor.getTextField().setEditable(false);
            spin.setEditor(dateEditor);
      
    >
    And no, the problem is not solved, but presently looks to me like a bug. But number of bug impressions turned out to be my own fault.
    I calculated the previous day using a Calendar beause I think you have to worry about Leap seconds/years when calculating previous day. You could end up only going back through 23 hours which is not enough.
  • kleopatra-JavaNet
    kleopatra-JavaNet Member Posts: 1,105 Gold Badge
    sabre150 wrote:

    I'm still not clear as to what you consider the problem to be.
    not quite clear why you are not clear : -)

    - start the program
    - try to spin to the "next" date (either by button or keyboard, doesnt matter)
    - expected: next day showing
    - actual: nothing happens

    as mentioned in my last post, the error (on part of the dev, IMO) is an incorrect min. So your approach goes into the right direction but is not general enough. Basically, all lower-precision date fields (compared to the formatter) have to be nulled to get the expected behaviour, which is to allow input >= min

    Cheers
    Jeanette
  • sabre150
    sabre150 Member Posts: 1,405
    edited August 2011
    Kleopatra wrote:
    sabre150 wrote:

    I'm still not clear as to what you consider the problem to be.
    not quite clear why you are not clear : -)

    - start the program
    - try to spin to the "next" date (either by button or keyboard, doesnt matter)
    - expected: next day showing
    - actual: nothing happens
    Using Ubuntu 11.04 with both JDK1.6.0_26 and 1.7.0 it advances to the next day!

    Edited by: sabre150 on 11-Aug-2011 05:56

    And running on Windows XP using JDK1.6.0_26 it advances to the next day!
  • Jörg
    Jörg Member Posts: 1,301
    @Walter
    These damn non-native speakers always cause trouble ;-) How if I use "digit" instead of "cipher"?
    In German at least we use "cypher" for a single digit, wheras a "number" can have any count of digits.
    Do English natives make this difference, too?<br><br>

    @Sabre
    Using Ubuntu 11.04 with both JDK1.6.0_26 and 1.7.0 it advances to the next day!
    Now I understand. You don't meet what I experience under WIN7.
    Still strange that the jdks behave differently.<br><br>

    @Jeanette
    Thank you, that's it. So the date of the lower bound has to have hh:mm:ss:ms all set to zero - then it works.
    Great!
  • sabre150
    sabre150 Member Posts: 1,405
    edited August 2011
    >
    > So your approach goes into the right direction but is not general enough. Basically, all lower-precision date fields (compared to the formatter) have to be nulled to get the expected behaviour, which is to allow input >= min

    All the minor places have a value of zero on both Windows and Ubuntu. I do nothing to force this. For example, printing out the dates using the java.util.Date.toString() method I get
    Fri Aug 12 00:00:00 BST 2011
    Sat Aug 13 00:00:00 BST 2011
    Sun Aug 14 00:00:00 BST 2011
  • sabre150
    sabre150 Member Posts: 1,405
    edited August 2011
    Jörg wrote:

    @Jeanette
    Thank you, that's it. So the date of the lower bound has to have hh:mm:ss:ms all set to zero - then it works.
    Great!
    That is not what my test harness shows.
        public DateSpinner()
        {
            setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            
            GregorianCalendar nowCalendar = new GregorianCalendar();
            Date now = nowCalendar.getTime();
            nowCalendar.add(Calendar.DAY_OF_MONTH, -1);
            Date thisTimeYesterday = nowCalendar.getTime();
            final SpinnerDateModel spinnerDateModel = new SpinnerDateModel(now, thisTimeYesterday, null, Calendar.DAY_OF_MONTH);
            JSpinner spin = new JSpinner(spinnerDateModel);
            JSpinner.DateEditor dateEditor = new JSpinner.DateEditor(spin, "dd.MM.yyyy");
            dateEditor.getTextField().setEditable(false);
            spin.setEditor(dateEditor);
            spin.setPreferredSize(new Dimension(200, 100));
    
            spinnerDateModel.addChangeListener(new ChangeListener()
            {
                @Override
                public void stateChanged(ChangeEvent ev)
                {
                    System.out.println((Date) spinnerDateModel.getValue());
                }
    
            });
    
            getContentPane().add(spin);
            pack();
            setLocationRelativeTo(null);
            setVisible(true);
        }
  • kleopatra-JavaNet
    kleopatra-JavaNet Member Posts: 1,105 Gold Badge
    edited August 2011
    sabre150 wrote:
    Jörg wrote:

    @Jeanette
    Thank you, that's it. So the date of the lower bound has to have hh:mm:ss:ms all set to zero - then it works.
    Great!
    That is not what my test harness shows.
    that's because you changed the original setup by moving the min into the past, thus solving the problem in that specific context :-)

    Doing so, the initial value is valid in the coordinate system of the formatter. So the very first parsing succeeds and normalizes the "now" to have its time fields zeroed. With that initial value valid, all is fine.

    Just to sure that's really so: run your test with now for both value and min - how does it behave?

    Cheers
    Jeanette
  • kleopatra-JavaNet
    kleopatra-JavaNet Member Posts: 1,105 Gold Badge
    edited August 2011
    [deleted duplicate content - forgot that I already mentioned what I repeated here ;]

    Edited by: Kleopatra on Aug 11, 2011 3:44 PM
  • walterln
    walterln Member Posts: 2,302
    Jörg wrote:
    @Walter
    These damn non-native speakers always cause trouble ;-) How if I use "digit" instead of "cipher"?
    In German at least we use "cypher" for a single digit, wheras a "number" can have any count of digits.
    Do English natives make this difference, too?<br><br>
    I'm not an native English speaker either, but yeah I think digit is a better translation for a single digit number ;-).
  • DrClap
    DrClap Member Posts: 25,479
    Jörg wrote:
    @Walter
    These damn non-native speakers always cause trouble ;-) How if I use "digit" instead of "cipher"?
    In German at least we use "cypher" for a single digit, wheras a "number" can have any count of digits.
    Do English natives make this difference, too?
    Yes, we do. A "digit" is anything from "0" to "9". Only to make it more confusing, a "number" can have any number of digits and "digit" also means "finger". ;-)
  • Jörg
    Jörg Member Posts: 1,301
    Thank you.
    And "cipher" can be an encrypted sign as well as digit?
  • darrylburke
    darrylburke Member Posts: 18,007
    Jörg wrote:
    Thank you.
    And "cipher" can be an encrypted sign as well as digit?
    The number zero, yes. Any old digit, no.
    http://en.wikipedia.org/wiki/Names_for_the_number_0_in_English#.22Zero.22_and_.22cipher.22

    db
  • DrClap
    DrClap Member Posts: 25,479
    Jörg wrote:
    Thank you.
    And "cipher" can be an encrypted sign as well as digit?
    "Cipher" usually means a method of encryption. As in "Apply this cipher to the text so the Germans can't read it."
  • Jörg
    Jörg Member Posts: 1,301
    Thank you all.
  • kleopatra-JavaNet
    kleopatra-JavaNet Member Posts: 1,105 Gold Badge
    An alternative to manipulating the "higher-resolution" time fields manually might be to
    - initialize the spinnerModel without bounds
    - initialize the spinner and its editor
    - force a commit and then set the min to the current value
            SpinnerDateModel model = new SpinnerDateModel(date, 
                    null,  null, Calender.DAY_OF_MONTH);
            spin = new JSpinner(model);
            spin.setEditor(new JSpinner.DateEditor(spin, "dd.MM.yyyy"));
            try {
                spin.commitEdit();
                model.setStart((Comparable) spin.getValue());
            } catch (ParseException e) {
                // will not happen, the initial value is valid
                e.printStackTrace();
            }
    Actually, I think the JSpinner not committing its initial value and/or not committing on setting the editor is a bug. And missing api to not be able to set the formatter except by setting the editor ...

    Cheers
    Jeanette
  • splungebob
    splungebob Member Posts: 121
    DrClap wrote:
    Jörg wrote:
    @Walter
    These damn non-native speakers always cause trouble ;-) How if I use "digit" instead of "cipher"?
    In German at least we use "cypher" for a single digit, wheras a "number" can have any count of digits.
    Do English natives make this difference, too?
    Yes, we do. A "digit" is anything from "0" to "9". Only to make it more confusing, a "number" can have any number of digits and "digit" also means "finger". ;-)
    I can dig it.
  • Jörg
    Jörg Member Posts: 1,301
    @Kleopatra

    I've noted down this way, too. But I sympathize with the first solution as it's "less complex" ;-)
    Have a nice week-end

    Jörg
This discussion has been closed.