Java News from Saturday, January 17, 2004

This coming Wednesday, January 21, I will be speaking to the XML Developers Network of the Capital District in Albany, New York. The topic is "XQuery: Exquisite or Excruciating?". I'll be exploring XQuery 1.0 and demonstrating XQuisitor, an open source Swing GUI tool for querying XML that I wrote based on Michael Kay's Saxon 7.0. This has inspired me to try to fix a few lingering issues in XQuisitor. The most annoying is indicated in this screenshot:

The labels don't line up.

If you look closely, you'll noitce that the labels for "Base URI" and "Context document" don't quite line up, nor do their respective text fields. The items in the bottom row are about one pixel to the right of the matching items in the top row. I have wasted a ridiculous amount of time trying to fix this using a variety of layout managers, and so far not succeeded. Currently, I'm using a GridBagLayout with the relevant code as follows:

private JPanel makeOptionsPanel() {
  
  JPanel options = new JPanel();
  GridBagConstraints constraints = new GridBagConstraints();
  GridBagLayout gbl = new GridBagLayout();
  options.setLayout(gbl);
  
  // It looks like each text field needs its own
  // UndoManager which is active whenever that field has the focus.
  // Otherwise, the undo crosses field boundaries????
  // baseField.getDocument().addUndoableEditListener(manager);

  JLabel baseLabel = new JLabel(
    Messages.getString("Base_URI___9"), JLabel.LEFT); 
  baseLabel.setDisplayedMnemonic(KeyEvent.VK_B);
  baseLabel.setLabelFor(baseField);
  
  JLabel chooserLabel = new JLabel(
    Messages.getString("Context___10"), JLabel.LEFT); 
  chooserLabel.setDisplayedMnemonic(KeyEvent.VK_C);
  chooserLabel.setLabelFor(contextField);
  
  constraints.gridx=0;
  constraints.gridy=0;
  constraints.gridwidth=3;
  constraints.gridheight=1;
  constraints.anchor = GridBagConstraints.WEST;
  gbl.setConstraints(baseLabel, constraints);
  options.add(baseLabel);
  
  constraints.anchor = GridBagConstraints.CENTER;
  constraints.gridx=3;
  constraints.gridy=0;
  constraints.gridwidth=4;
  constraints.gridheight=1;
  gbl.setConstraints(baseField, constraints);
  options.add(baseField);
  
  JButton chooseBase = new JButton(Messages.getString("..._11")); 
  constraints.gridx=7;
  constraints.gridy=0;
  constraints.gridwidth=1;
  constraints.gridheight=1;
  constraints.insets = new Insets(0, 2, 2, 0); 
  gbl.setConstraints(chooseBase, constraints);
  options.add(chooseBase);
  chooseBase.addActionListener(new BaseURIChooser());
  
  // contextField.getDocument().addUndoableEditListener(manager);
  constraints.insets = new Insets(0, 0, 0, 0); // bug fix
  constraints.gridx=0;
  constraints.gridy=1;
  constraints.gridwidth=3;
  constraints.gridheight=1;
  constraints.anchor = GridBagConstraints.WEST;
  gbl.setConstraints(chooserLabel, constraints);
  options.add(chooserLabel);
  constraints.anchor = GridBagConstraints.CENTER;
  constraints.gridx=3;
  constraints.gridy=1;
  constraints.gridwidth=4;
  constraints.gridheight=1;
  gbl.setConstraints(contextField, constraints);
  options.add(contextField);
  JButton chooseFile = new JButton(Messages.getString("..._12")); 
  constraints.gridx=7;
  constraints.gridy=1;
  constraints.gridwidth=1;
  constraints.gridheight=1;
  gbl.setConstraints(chooseFile, constraints);
  options.add(chooseFile);
  chooseFile.addActionListener(new ContextChooser());
  
  ItemListener redrawer = new NeedsSerialization();
  doWrapping.addItemListener(redrawer);
  doWrapping.setMnemonic(KeyEvent.VK_W);
  doIndenting.addItemListener(redrawer);
  doIndenting.setMnemonic(KeyEvent.VK_P);
  
  constraints.gridx=0;
  constraints.gridy=2;
  constraints.gridwidth=3;
  constraints.gridheight=1;
  constraints.anchor = GridBagConstraints.SOUTHWEST;
  gbl.setConstraints(doWrapping, constraints);
  options.add(doWrapping);

  constraints.gridx=0;
  constraints.gridy=3;
  constraints.gridwidth=3;
  constraints.gridheight=1;
  gbl.setConstraints(doIndenting, constraints);
  options.add(doIndenting);
  JButton executeButton = new JButton(Messages.getString("Run_Query_13"));
  executeButton.addActionListener(new RunQuery());
  constraints.gridx=0;
  constraints.gridy=4;
  constraints.gridwidth=3;
  constraints.gridheight=1;
  gbl.setConstraints(executeButton, constraints);
  options.add(executeButton);        

  String userdir = System.getProperty("user.dir"); 
  if (userdir != null) {
      File baseDir = new File(userdir);
      URI baseURI = baseDir.toURI();
      baseField.setText(baseURI.toASCIIString());
  }
  
  // baseField.set(KeyEvent.VK_B);
  
  JPanel master = new JPanel();
  master.add(options);
  return master;
}

Does anyone have any idea what I need to do in order to get these user interface widgets to line up pixel perfect? Send suggestions to elharo@metalab.unc.edu. Thanks.

Update: Jochen Bedersdorfer gave me half the answer. Aligning the field to the EAST rather than the CENTER lines them up. However, this doesn't work for the labels because it aligns their right edges rather than their left, and unlike the field the labels don't have the same width. I'm still looking for the full solution.

And now I have it. "dvholten" noticed that I was carrying the insets from chooseBase button down to the chooserLabel, and then to all other items added later. That was my bug. It is now fixed as I will prove on Wednesday in Albany. It's reassuring to note that the GridBagLayout does actually work, and it was just my stupid code bug that made it fail to align. This would not have happened if I had not followed the bad practice of reusing the same variable for different objects.


The Jakarta Apache Project has posted the third release candidate of HTTPClient 2.0. "Although the java.net package provides basic functionality for accessing resources via HTTP, it doesn't provide the full flexibility or functionality needed by many applications. The Jakarta Commons HttpClient component seeks to fill this void by providing an efficient, up-to-date, and feature-rich package implementing the client side of the most recent HTTP standards and recommendations....Designed for extension while providing robust support for the base HTTP protocol, the HttpClient component may be of interest to anyone building HTTP-aware client applications such as web browsers, web service clients, or systems that leverage or extend the HTTP protocol for distributed communication." This version fixes various bugs.