All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.numdata.oss.ui.AbstractRadioWizardPage Maven / Gradle / Ivy

There is a newer version: 1.22
Show newest version
/*
 * Copyright (c) 2017, Numdata BV, The Netherlands.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Numdata nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NUMDATA BV BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.numdata.oss.ui;

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

import com.numdata.oss.*;
import org.jetbrains.annotations.*;

/**
 * This wizard page is used for selecting a value using radio buttons.
 *
 * @author Peter S. Heijnen
 */
public abstract class AbstractRadioWizardPage
extends WizardPage
implements ItemListener
{
	/**
	 * Values from which a selection is made.
	 */
	private Object[] _values;

	/**
	 * Panel containing radio buttons.
	 */
	private JPanel _radioPanel;

	/**
	 * Radio buttons used to make selection.
	 */
	private JRadioButton[] _radios;

	/**
	 * Construct page.
	 *
	 * @param wizard      Wizard that controls this page.
	 * @param res         Resource bundle for this page (optional).
	 * @param resourceKey Resource key (name) of page (optional).
	 */
	protected AbstractRadioWizardPage( final Wizard wizard, final ResourceBundle res, final String resourceKey )
	{
		super( wizard, res, resourceKey, new BorderLayout() );

		_values = null;
		_radioPanel = null;
		_radios = null;
	}

	protected void initGui()
	{
		add( createRadioPanel(), BorderLayout.CENTER );
	}

	protected void shown()
	{
		updateRadios();
		updateValue();
		setEnabled( ( _values != null ) && ( _values.length > 1 ) );
	}

	/**
	 * Create panel on which the radio buttons will be placed.
	 *
	 * @return {@link JPanel} on which the radio buttons will be placed.
	 */
	protected JPanel createRadioPanel()
	{
		final JPanel result = new RadioPanel( _res.getString( getName() + ".message" ), this );
		_radioPanel = result;
		return result;
	}

	/**
	 * Create radio button that will be placed on the radio panel.
	 *
	 * @param name Name of the radio button.
	 * @param text Text of the radio button.
	 *
	 * @return The created {@link JRadioButton} (never {@code null}).
	 */
	protected JRadioButton createRadioButton( final String name, final String text )
	{
		final JRadioButton result = new JRadioButton( text );
		result.setFocusPainted( false );
		result.setName( name );
		return result;
	}

	/**
	 * Update contents of radio panel based on the currently available choices.
	 */
	protected void updateRadios()
	{
		final Wizard wizard = getWizard();
		final Locale locale = wizard.getLocale();
		final ResourceBundle res = _res;
		final String resourceKey = getName();

		final JPanel radioPanel = _radioPanel;
		final Object[] values = getValues();
		final int selectedIndex = Math.max( 0, ArrayTools.indexOf( getValue(), values ) );
		final JRadioButton[] radios = new JRadioButton[ values.length ];

		final String valuePattern = ResourceBundleTools.getString( res, resourceKey + ".value", null );
		final MessageFormat valueFormat = ( valuePattern != null ) ? new MessageFormat( valuePattern, locale ) : null;

		radioPanel.removeAll();

		for ( int i = 0; i < values.length; i++ )
		{
			Object value = values[ i ];
			final String name;

			if ( value instanceof Object[] )
			{
				final Object[] valueObjects = (Object[])value;

				name = ( valueObjects.length > 0 ) ? String.valueOf( valueObjects[ 0 ] ) : null;

				if ( valueObjects.length == 1 )
				{
					value = getTranslatedValue( valueObjects[ 0 ] );
				}
				else
				{
					final Object[] translatedValues = new Object[ valueObjects.length ];

					for ( int j = 0; j < translatedValues.length; j++ )
					{
						translatedValues[ j ] = getTranslatedValue( valueObjects[ j ] );
					}

					value = translatedValues;
				}
			}
			else
			{
				name = ( value != null ) ? String.valueOf( value ) : null;
				value = getTranslatedValue( value );

			}

			final String text = getText( value, locale, valueFormat );

			final JRadioButton radio = createRadioButton( name, text );
			radioPanel.add( radio );
			radio.setSelected( i == selectedIndex );
			radios[ i ] = radio;
		}

		_values = values;
		_radios = radios;
	}

	/**
	 * Returns a text representation of the given value.
	 *
	 * @param value  Value to create a representation of.
	 * @param locale Locale to be used.
	 * @param format Suggested message format for the text representation;
	 *               {@code null} if not available.
	 *
	 * @return Text representation of the given value.
	 */
	protected String getText( final Object value, final Locale locale, final MessageFormat format )
	{
		return ( format != null ) ? format.format( ( value instanceof Object[] ) ? value : new Object[] { value } )
		                          : ArrayTools.isValidType( value ) ? ArrayTools.toString( value )
		                                                            : String.valueOf( value );
	}

	/**
	 * Update selection using the currently selected radio button.
	 *
	 * @see #getRadioIndex
	 * @see #setValue
	 */
	protected void updateValue()
	{
		final int radioIndex = getRadioIndex();
		setValue( ( radioIndex < 0 ) ? null : _values[ radioIndex ] );
	}

	/**
	 * Get index of currently selected radio button.
	 *
	 * @return Index of radio button; {@code -1} if no radio button is currently
	 * selected.
	 */
	protected final int getRadioIndex()
	{
		int result = -1;

		final JRadioButton[] radios = _radios;
		if ( radios != null )
		{
			for ( int i = 0; i < radios.length; i++ )
			{
				final JRadioButton radio = radios[ i ];
				if ( ( radio != null ) && radio.isSelected() )
				{
					result = i;
					break;
				}
			}
		}

		return result;
	}

	public final void itemStateChanged( final ItemEvent event )
	{
		if ( ( event.getID() == ItemEvent.ITEM_STATE_CHANGED ) && ( event.getStateChange() == ItemEvent.SELECTED ) )
		{
			updateValue();
		}
	}


	/**
	 * Get currently selected value.
	 *
	 * @return Currently selected value; {@code null} if no value has been
	 * selected.
	 */
	protected abstract Object getValue();

	/**
	 * Set currently selected value.
	 *
	 * @param value Value to select ({@code null} to clear the selection).
	 */
	protected abstract void setValue( final Object value );

	/**
	 * Get list of values to make the selection from.
	 *
	 * @return List of values (never {@code null}).
	 */
	protected abstract Object[] getValues();

	/**
	 * Panel with optional title that automatically lays out (radio) buttons and
	 * registers an optional listener to all (radio) buttons on the panel.
	 */
	private static class RadioPanel
	extends JPanel
	{
		/**
		 * Button group for all (radio) buttons.
		 */
		final ButtonGroup _buttonGroup;

		/**
		 * Default constraints used for buttons.
		 */
		final GridBagConstraints _constraints;

		/**
		 * Listener that is registered to all buttons.
		 */
		@Nullable
		private final ItemListener _itemListener;

		/**
		 * Construct panel.
		 *
		 * @param title        Title for panel.
		 * @param itemListener Listener that is registered to all buttons.
		 */
		RadioPanel( @Nullable final String title, @Nullable final ItemListener itemListener )
		{
			super( new GridBagLayout() );

			_itemListener = itemListener;
			_buttonGroup = new ButtonGroup();

			final GridBagConstraints constraints = new GridBagConstraints();
			constraints.gridx = 0;
			constraints.weightx = 1.0;
			constraints.anchor = GridBagConstraints.WEST;
			_constraints = constraints;

			if ( title != null )
			{
				constraints.insets.left = 16;
				constraints.insets.right = 16;
				constraints.fill = GridBagConstraints.HORIZONTAL;

				final JLabel label = new JLabel( title );
				label.setBorder( new EtchedBorder( 0, 0, 8, 0 ) );
				add( label, constraints );

				constraints.insets.left = 30;
				constraints.insets.right = 30;
				constraints.fill = GridBagConstraints.NONE;
			}
		}

		protected void addImpl( final Component component, final Object constraints, final int index )
		{
			if ( component instanceof AbstractButton )
			{
				final AbstractButton button = (AbstractButton)component;
				button.setSelected( true );
				_buttonGroup.add( button );
				if ( _itemListener != null )
				{
					button.addItemListener( _itemListener );
				}
			}

			super.addImpl( component, ( constraints == null ) ? _constraints : constraints, index );
		}

		public void remove( final int index )
		{
			final Component component = getComponent( index );
			if ( component instanceof AbstractButton )
			{
				final AbstractButton button = (AbstractButton)component;
				if ( _itemListener != null )
				{
					button.removeItemListener( _itemListener );
				}
				_buttonGroup.remove( button );
			}

			super.remove( index );
		}

		public void removeAll()
		{
			synchronized ( getTreeLock() )
			{
				for ( int i = getComponentCount(); --i >= 0; )
				{
					final Component component = getComponent( i );
					if ( component instanceof AbstractButton )
					{
						remove( component );
					}
				}
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy