StarGroupSelectionPane.java
/**
* VStar: a statistical analysis tool for variable star data.
* Copyright (C) 2010 AAVSO (http://www.aavso.org/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.aavso.tools.vstar.ui.dialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.aavso.tools.vstar.ui.resources.StarGroups;
import org.aavso.tools.vstar.util.locale.LocaleProps;
/**
* This class represents a widget that permits a star group to be selected from
* a pop-up list and a star in that group from another pop-up list.
*/
@SuppressWarnings("serial")
public class StarGroupSelectionPane extends JPanel {
private final static String NO_STARS = "No stars";
private JComboBox<String> starGroupSelector;
private JComboBox<String> starSelector;
private ActionListener starGroupSelectorListener;
private ActionListener starSelectorListener;
private StarGroups starGroups;
// Selected star group, name and AUID.
private String selectedStarGroup;
private String selectedStarName;
private String selectedAUID;
private boolean clearStarField;
private JTextField starField;
/**
* Constructor
*
* @param starField An optional star field to be cleared when a group star is
* selected.
*/
public StarGroupSelectionPane(JTextField starField) {
this(starField, true);
}
/**
* Constructor
*
* @param starField An optional star field to be cleared or set when a
* group star is selected.
* @param clearStarField Whether to clear (true) or set (false) the star field.
*/
public StarGroupSelectionPane(JTextField starField, boolean clearStarField) {
this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
this.setBorder(BorderFactory.createEtchedBorder());
selectedStarGroup = null;
selectedStarName = null;
selectedAUID = null;
this.clearStarField = clearStarField;
this.starField = starField;
starGroups = StarGroups.getInstance();
Set<String> starGroupMapKeys = starGroups.getGroupNames();
starGroupSelector = new JComboBox<String>(starGroupMapKeys.toArray(new String[0]));
selectedStarGroup = (String) starGroupSelector.getItemAt(0);
starGroupSelector.setBorder(BorderFactory.createTitledBorder(LocaleProps.get("NEW_STAR_FROM_AID_DLG_GROUP")));
starGroupSelectorListener = createStarGroupSelectorListener();
starGroupSelector.addActionListener(starGroupSelectorListener);
starSelector = new JComboBox<String>();
starSelector.setBorder(BorderFactory.createTitledBorder(LocaleProps.get("NEW_STAR_FROM_AID_DLG_STAR")));
starSelectorListener = createStarSelectorListener();
populateStarListForSelectedGroup();
starSelector.addActionListener(starSelectorListener);
this.add(starGroupSelector);
this.add(starSelector);
}
// Star group selector listener.
private ActionListener createStarGroupSelectorListener() {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Populate the star selector list according
// to the selected group.
String group = (String) starGroupSelector.getSelectedItem();
// Avoid clearing the star list / selectedStarName when the combo fires
// with no selection (can happen around dialog show/layout); that would
// wipe the last star and break persistence across invocations.
if (group != null) {
selectedStarGroup = group;
populateStarListForSelectedGroup();
updateStarFieldForSelection();
}
}
};
}
// Star selector listener.
private ActionListener createStarSelectorListener() {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
String starName = (String) starSelector.getSelectedItem();
if (starName == null || NO_STARS.equals(starName) || selectedStarGroup == null) {
return;
}
selectedStarName = starName;
selectedAUID = starGroups.getAUID(selectedStarGroup, selectedStarName);
updateStarFieldForSelection();
}
};
}
private void withSuppressedStarSelectorListener(Runnable action) {
boolean wasRegistered = removeAllStarSelectorListeners();
try {
action.run();
} finally {
if (wasRegistered) {
starSelector.addActionListener(starSelectorListener);
}
}
}
private void withSuppressedGroupSelectorListener(Runnable action) {
boolean wasRegistered = removeAllGroupSelectorListeners();
try {
action.run();
} finally {
if (wasRegistered) {
starGroupSelector.addActionListener(starGroupSelectorListener);
}
}
}
private boolean removeAllStarSelectorListeners() {
boolean removedAny = false;
for (ActionListener listener : starSelector.getActionListeners()) {
if (listener == starSelectorListener) {
starSelector.removeActionListener(listener);
removedAny = true;
}
}
return removedAny;
}
private boolean removeAllGroupSelectorListeners() {
boolean removedAny = false;
for (ActionListener listener : starGroupSelector.getActionListeners()) {
if (listener == starGroupSelectorListener) {
starGroupSelector.removeActionListener(listener);
removedAny = true;
}
}
return removedAny;
}
private void syncSelectionFromUI() {
Object group = starGroupSelector.getSelectedItem();
if (group instanceof String && starGroups.doesGroupExist((String) group)) {
selectedStarGroup = (String) group;
}
Object star = starSelector.getSelectedItem();
if (star instanceof String && !NO_STARS.equals(star) && selectedStarGroup != null
&& starGroups.doesStarExistInGroup(selectedStarGroup, (String) star)) {
selectedStarName = (String) star;
selectedAUID = starGroups.getAUID(selectedStarGroup, selectedStarName);
}
}
private void updateStarFieldForSelection() {
if (starField != null) {
if (clearStarField) {
starField.setText("");
} else if (selectedStarName != null) {
starField.setText(selectedStarName);
}
}
}
/**
* Populate the star list combo-box given the currently selected star group. If
* the previously selected star is still in this group (e.g. after
* {@link #refreshGroups()} or prefs-driven star-group updates), keep that
* selection instead of resetting to the first list entry.
*/
public void populateStarListForSelectedGroup() {
withSuppressedStarSelectorListener(() -> {
starSelector.removeAllItems();
if (selectedStarGroup != null && !starGroups.getStarNamesInGroup(selectedStarGroup).isEmpty()) {
for (String starName : starGroups.getStarNamesInGroup(selectedStarGroup)) {
starSelector.addItem(starName);
}
// Prefer the model's string instance so setSelectedIndex/Item matches reliably.
String nameToSelect = findStarNameToSelect(selectedStarGroup, selectedStarName);
if (nameToSelect == null && starSelector.getItemCount() > 0) {
nameToSelect = (String) starSelector.getItemAt(0);
}
if (nameToSelect != null) {
int idx = indexOfStarItem(nameToSelect);
if (idx >= 0) {
starSelector.setSelectedIndex(idx);
nameToSelect = (String) starSelector.getItemAt(idx);
} else {
starSelector.setSelectedIndex(0);
nameToSelect = (String) starSelector.getItemAt(0);
}
}
selectedStarName = nameToSelect;
if (selectedStarName != null) {
selectedAUID = starGroups.getAUID(selectedStarGroup, selectedStarName);
} else {
selectedAUID = null;
}
} else {
starSelector.addItem(NO_STARS);
selectedStarName = null;
selectedAUID = null;
}
});
}
/**
* Pick a star name to show: keep {@code desiredName} if it matches a star in
* the group (exact or trim), else null so the caller can fall back to the first
* star.
*/
private String findStarNameToSelect(String groupName, String desiredName) {
if (desiredName == null || !starGroups.doesStarExistInGroup(groupName, desiredName)) {
String trimmed = desiredName == null ? null : desiredName.trim();
if (trimmed != null && starGroups.doesStarExistInGroup(groupName, trimmed)) {
return trimmed;
}
for (String key : starGroups.getStarNamesInGroup(groupName)) {
if (key != null && trimmed != null && key.equalsIgnoreCase(trimmed)) {
return key;
}
}
return null;
}
return desiredName;
}
private int indexOfStarItem(String starName) {
for (int i = 0; i < starSelector.getItemCount(); i++) {
Object o = starSelector.getItemAt(i);
if (starName.equals(o)) {
return i;
}
}
return -1;
}
/**
* Add the specified group (to the map and visually) if it does not exist.
*
* @param groupName The group to add.
*/
public void addGroup(String groupName) {
if (!starGroups.doesGroupExist(groupName)) {
starGroups.addStarGroup(groupName);
starGroupSelector.addItem(groupName);
selectAndRefreshStarsInGroup(groupName);
}
}
/**
* Remove the specified group (from the map and visually) if it exists.
*
* @param groupName The group to remove.
*/
public void removeGroup(String groupName) {
if (starGroups.doesGroupExist(groupName)) {
if (MessageBox.showConfirmDialog("Remove Group", LocaleProps.get("REALLY_DELETE"))) {
starGroups.removeStarGroup(groupName);
starGroupSelector.removeItem(groupName);
if (starGroupSelector.getItemCount() > 0) {
selectAndRefreshStarsInGroup((String) starGroupSelector.getItemAt(0));
}
}
}
}
/**
* Add the specified group-star-AUID triple.
*
* @param groupName The group to add.
* @param starName The star to add to the specified group.
* @param auid The AUID of the star to be added.
*/
public void addStar(String groupName, String starName, String auid) {
if (starGroups.doesGroupExist(groupName)) {
starGroups.addStar(groupName, starName, auid);
selectAndRefreshStarsInGroup(groupName);
}
}
/**
* Remove the specified star in the specified group.
*
* @param groupName The group to add.
* @param starName
*/
public void removeStar(String groupName, String starName) {
if (starGroups.doesGroupExist(groupName)) {
if (MessageBox.showConfirmDialog("Remove Star", LocaleProps.get("REALLY_DELETE"))) {
starGroups.removeStar(groupName, starName);
selectAndRefreshStarsInGroup(groupName);
}
}
}
/**
* Clear the groups in the star group selector list.
*/
public void resetGroups() {
starGroups.resetGroupsToDefault();
withSuppressedGroupSelectorListener(() -> {
starGroupSelector.removeAllItems();
for (String groupName : starGroups.getGroupNames()) {
starGroupSelector.addItem(groupName);
}
selectAndRefreshStarsInGroup(starGroups.getDefaultStarListTitle());
});
}
/**
* Refresh the groups in the star group selector list. Only groups with stars
* will be "refreshed".
*/
public void refreshGroups() {
syncSelectionFromUI();
// Rebuilding the combo fires ActionListeners; without removing them first,
// selectedStarGroup is overwritten (often to the first group) before we
// can restore the user's last choice across dialog invocations.
final String savedGroup = getSelectedStarGroupName();
final String savedStar = getSelectedStarName();
withSuppressedGroupSelectorListener(() -> {
starGroupSelector.removeAllItems();
for (String groupName : starGroups.getGroupNames()) {
if (!starGroups.getStarNamesInGroup(groupName).isEmpty()) {
starGroupSelector.addItem(groupName);
}
}
String groupToSelect = resolveGroupToSelect(savedGroup);
selectedStarName = savedStar;
if (groupToSelect != null) {
starGroupSelector.setSelectedItem(groupToSelect);
selectedStarGroup = groupToSelect;
populateStarListForSelectedGroup();
}
});
}
private String resolveGroupToSelect(String savedGroup) {
if (savedGroup != null && starGroups.doesGroupExist(savedGroup)
&& !starGroups.getStarNamesInGroup(savedGroup).isEmpty() && groupAppearsInCombo(savedGroup)) {
return savedGroup;
}
String def = starGroups.getDefaultStarListTitle();
if (groupAppearsInCombo(def)) {
return def;
}
if (starGroupSelector.getItemCount() > 0) {
return (String) starGroupSelector.getItemAt(0);
}
return null;
}
private boolean groupAppearsInCombo(String groupName) {
if (groupName == null) {
return false;
}
for (int i = 0; i < starGroupSelector.getItemCount(); i++) {
if (groupName.equals(starGroupSelector.getItemAt(i))) {
return true;
}
}
return false;
}
/**
* Select the specified group and refresh its stars.
*
* @param groupName The group to select.
*/
public void selectAndRefreshStarsInGroup(String groupName) {
if (starGroups.doesGroupExist(groupName)) {
withSuppressedGroupSelectorListener(() -> {
starGroupSelector.setSelectedItem(groupName);
selectedStarGroup = groupName;
populateStarListForSelectedGroup();
});
}
}
/**
* @return the starGroups
*/
public StarGroups getStarGroups() {
return starGroups;
}
/**
* @return the selectedStarGroup
*/
public String getSelectedStarGroupName() {
return selectedStarGroup;
}
/**
* @return the selectedStarName
*/
public String getSelectedStarName() {
return selectedStarName;
}
/**
* @return the selectedAUID
*/
public String getSelectedAUID() {
return selectedAUID;
}
}