/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* 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 com.rapidminer.gui.properties;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.ScrollPaneConstants;
import com.rapidminer.RapidMiner;
import com.rapidminer.gui.properties.SettingsItem.Type;
import com.rapidminer.gui.tools.ExtendedJScrollPane;
import com.rapidminer.gui.tools.ExtendedJTabbedPane;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.components.ToolTipWindow;
import com.rapidminer.gui.tools.components.ToolTipWindow.TipProvider;
import com.rapidminer.gui.tools.components.ToolTipWindow.TooltipLocation;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.ParameterService;
/**
* The tabs for the different groups of RapidMiner settings. Each tab contains a
* {@link SettingsPropertyPanel} for the settings in this group.
*
* @author Sebastian Land, Ingo Mierswa, Adrian Wilke
*/
public class SettingsTabs extends ExtendedJTabbedPane {
private static final long serialVersionUID = -229446448782516589L;
private final List<SettingsPropertyPanel> parameterPanels = new LinkedList<>();
private final Map<String, Integer> groupKeysToTabIndexMap = new HashMap<>();
private static Map<JComponent, String> tooltipDescriptions = new HashMap<>();
/** The containing dialog */
private final SettingsDialog settingsDialog;
/** Color matching the icons */
public static final Color COLOR_SUBGROUP = new Color(32, 100, 148);
/** Color matching the tabs border */
public static final Color COLOR_GROUP_DESCRIPTION_BORDER = new Color(220, 220, 224);
/** Color for the backgournd of the tab description */
public static final Color COLOR_GROUP_DESCRIPTION_BACKGROUND = new Color(230, 230, 234);
/** Color of the group/tab descriptions */
public static final Color COLOR_GROUP_DESCRIPTION = SwingTools.RAPIDMINER_GRAY;
/** Compares titles of SettingItem objects */
private static final Comparator<SettingsItem> SETTINGS_ITEM_COMPARATOR = new Comparator<SettingsItem>() {
@Override
public int compare(SettingsItem itemA, SettingsItem itemB) {
if (itemA == null && itemB == null) {
return 0;
} else if (itemA == null) {
return 1;
} else if (itemB == null) {
return -1;
} else {
return itemA.getTitle().compareTo(itemB.getTitle());
}
}
};
/**
* Creates necessary {@link SettingsItem}s and related tabs for the settings.
*
* @param settingsDialog
* The containing dialog. Is used to create {@link ToolTipWindow}s for tabs.
*/
public SettingsTabs(SettingsDialog settingsDialog) {
this(settingsDialog, null, null);
}
/**
* Creates necessary {@link SettingsItem}s and related tabs for the settings.
*
* @param settingsDialog
* The containing dialog. Is used to create {@link ToolTipWindow}s for tabs.
* @param filter
* Used to filter the setting parameters
* @param cache
* which should be used to retrieve the values
*/
public SettingsTabs(SettingsDialog settingsDialog, String filter, Properties propertyCache) {
this.settingsDialog = settingsDialog;
setTabPlacement(JTabbedPane.LEFT);
// Get defined-parameters
// These are all parameters, which will appear in dialog
Collection<String> definedParameterKeys = ParameterService.getDefinedParameterKeys();
// Get parsed settings items
SettingsItems items = SettingsItems.INSTANCE;
// Remove structured settings items, which are not in defined-parameter
// (The structure is known, but they would not be used)
items.removeParameterInverse(definedParameterKeys);
// Get defined-parameters, which are not known in XML structure
// (They will be attached, even if the structure is not known)
Collection<String> structuredKeys = items.getKeys();
Iterator<String> iterator = definedParameterKeys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
if (structuredKeys.contains(key)) {
iterator.remove();
}
}
// Generate structure for remaining defined-parameters
boolean isDebugMode = Boolean
.parseBoolean(ParameterService.getParameterValue(RapidMiner.PROPERTY_RAPIDMINER_GENERAL_DEBUGMODE));
iterator = definedParameterKeys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
SettingsItems.createAndAddItem(key, Type.PARAMETER);
if (isDebugMode) {
LogService.getRoot().log(Level.WARNING, "com.rapidminer.gui.properties.SettingsTabs.no_parameter_in_xml",
key);
}
}
// Remove empty subgroups and groups
items.clean();
// Create tabs
List<SettingsItem> groups = items.getItems(Type.GROUP);
if (!items.isStudioXmlParsedSuccessfully()) {
// Sort groups lexicographically, if XML could not be parsed
Collections.sort(groups, SETTINGS_ITEM_COMPARATOR);
}
for (SettingsItem group : groups) {
List<SettingsItem> subGroups = group.getChildren(Type.SUB_GROUP, filter);
List<SettingsItem> parameters = group.getChildren(Type.PARAMETER, filter);
boolean isSubGroupsEmpty = true;
for (SettingsItem subGroup : subGroups) {
if (!subGroup.getChildren(Type.PARAMETER, filter).isEmpty()) {
isSubGroupsEmpty = false;
break;
}
}
if (parameters.size() > 0 || !isSubGroupsEmpty) {
createTab(group.getKey(), group.getTitle(), group.getDescription(), subGroups, parameters, filter,
propertyCache);
}
}
// Remove the used flag to prevent broken settings dialog after second opening.
for (String key : SettingsItems.INSTANCE.getKeys()) {
SettingsItems.INSTANCE.get(key).setUsedInDialog(false);
}
}
/**
* Selects the tab, which is related to the specified groupKey.
*
* @param groupKey
* A key of a preferences group.
*/
public void selectTab(String groupKey) {
if (groupKeysToTabIndexMap.containsKey(groupKey)) {
setSelectedIndex(groupKeysToTabIndexMap.get(groupKey));
}
}
/**
* Creates a tab
*/
private void createTab(String groupKey, String groupTitle, String groupDescription, List<SettingsItem> subGroups,
List<SettingsItem> parameters, String filter, Properties propertyCache) {
JPanel containerPanel = new JPanel();
containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.PAGE_AXIS));
containerPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
// Add group description
JPanel descriptionPanel = new JPanel();
descriptionPanel.setLayout(new BoxLayout(descriptionPanel, BoxLayout.LINE_AXIS));
descriptionPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, COLOR_GROUP_DESCRIPTION_BORDER));
if (groupDescription != null && !groupDescription.isEmpty()) {
JLabel descriptionLabel = new JLabel();
descriptionLabel.setForeground(COLOR_GROUP_DESCRIPTION);
descriptionLabel.setText(groupDescription);
descriptionLabel.setBorder(BorderFactory.createEmptyBorder(6, 3, 6, 0));
descriptionPanel.add(descriptionLabel);
descriptionPanel.add(Box.createHorizontalGlue());
descriptionPanel.setBackground(COLOR_GROUP_DESCRIPTION_BACKGROUND);
} else {
Dimension dim = new Dimension(0, 1);
descriptionPanel.add(new Box.Filler(dim, dim, dim));
}
containerPanel.add(descriptionPanel);
final SettingsPropertyPanel table = new SettingsPropertyPanel(groupTitle, subGroups, parameters, filter,
propertyCache);
new ToolTipWindow(settingsDialog, new TipProvider() {
@Override
public String getTip(Object id) {
if (id == null) {
return null;
} else {
return SettingsTabs.tooltipDescriptions.get(id);
}
}
@Override
public Object getIdUnder(Point point) {
Point tableScreenLocation = table.getLocationOnScreen();
int mouseX = point.x + tableScreenLocation.x;
int mouseY = point.y + tableScreenLocation.y;
for (JComponent component : SettingsTabs.tooltipDescriptions.keySet()) {
if (!component.isShowing()) {
continue;
} else {
int compX = component.getLocationOnScreen().x;
int compY = component.getLocationOnScreen().y;
if (mouseX > compX && mouseY > compY && mouseX < compX + component.getWidth()
&& mouseY < compY + component.getHeight()) {
return component;
}
}
}
return null;
}
@Override
public Component getCustomComponent(Object id) {
return null;
}
}, table, TooltipLocation.RIGHT).setOnlyWhenFocussed(false).setReactOnMousePressed(true);
parameterPanels.add(table);
containerPanel.add(table);
ExtendedJScrollPane scrollPane = new ExtendedJScrollPane(containerPanel);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setPreferredSize(new Dimension(600, 300));
scrollPane.setBorder(null);
addTab(groupTitle, scrollPane);
groupKeysToTabIndexMap.put(groupKey, getTabCount() - 1);
}
public void applyProperties() {
for (SettingsPropertyPanel panel : parameterPanels) {
panel.applyProperties();
}
}
/**
* This method will save the parameters defined in this tab
*/
public void save() throws IOException {
applyProperties();
ParameterService.saveParameters();
}
/**
* Adds a tool tip description for a component
*/
public static void addToolTipDescription(JComponent component, String description) {
SettingsTabs.tooltipDescriptions.put(component, description);
}
}