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

weka.gui.SettingsEditor Maven / Gradle / Ivy

Go to download

The Waikato Environment for Knowledge Analysis (WEKA), a machine learning workbench. This is the stable version. Apart from bugfixes, this version does not receive any other breaking updates.

There is a newer version: 3.8.6
Show newest version
/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see .
 */

/*
 *    SettingsEditor.java
 *    Copyright (C) 2015 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.gui;

import weka.classifiers.Classifier;
import weka.clusterers.Clusterer;
import weka.core.Environment;
import weka.core.Settings;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Provides a panel for editing application and perspective settings
 * 
 * @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
 * @version $Revision: $
 */
public class SettingsEditor extends JPanel {

  private static final long serialVersionUID = 1453121012707399758L;

  protected GUIApplication m_ownerApp;
  protected Settings m_settings;
  protected JTabbedPane m_settingsTabs = new JTabbedPane();

  protected PerspectiveSelector m_perspectiveSelector;
  List m_perspectiveEditors =
    new ArrayList();

  public SettingsEditor(Settings settings, GUIApplication ownerApp) {
    setLayout(new BorderLayout());
    m_settings = settings;
    m_ownerApp = ownerApp;
    GenericObjectEditor.registerEditors();
    PropertyEditorManager.registerEditor(Color.class, ColorEditor.class);

    if (m_ownerApp.getPerspectiveManager().getLoadedPerspectives().size() > 0) {
      setupPerspectiveSelector();
    }
    setupPerspectiveSettings();
    add(m_settingsTabs, BorderLayout.CENTER);
  }

  public void applyToSettings() {
    if (m_perspectiveSelector != null) {
      m_perspectiveSelector.applyToSettings();
    }

    for (SingleSettingsEditor editor : m_perspectiveEditors) {
      editor.applyToSettings();
    }
  }

  protected void setupPerspectiveSelector() {
    m_perspectiveSelector = new PerspectiveSelector();
    m_settingsTabs.addTab("Perspectives", m_perspectiveSelector);
  }

  protected void setupPerspectiveSettings() {
    // any general settings? >1 because app settings have visible perspectives
    // settings
    if (m_settings.getSettings(m_ownerApp.getApplicationID()) != null
      && m_settings.getSettings(m_ownerApp.getApplicationID()).size() > 1) {
      Map appSettings =
        m_settings.getSettings(m_ownerApp.getApplicationID());
      SingleSettingsEditor appEditor = new SingleSettingsEditor(appSettings);
      m_settingsTabs.addTab("General", appEditor);
      m_perspectiveEditors.add(appEditor);
    }

    // main perspective
    Perspective mainPers = m_ownerApp.getMainPerspective();
    String mainTitle = mainPers.getPerspectiveTitle();
    String mainID = mainPers.getPerspectiveID();
    SingleSettingsEditor mainEditor =
      new SingleSettingsEditor(m_settings.getSettings(mainID));
    m_settingsTabs.addTab(mainTitle, mainEditor);
    m_perspectiveEditors.add(mainEditor);

    List availablePerspectives =
      m_ownerApp.getPerspectiveManager().getLoadedPerspectives();
    List availablePerspectivesIDs = new ArrayList();
    List availablePerspectiveTitles = new ArrayList();
    for (int i = 0; i < availablePerspectives.size(); i++) {
      Perspective p = availablePerspectives.get(i);
      availablePerspectivesIDs.add(p.getPerspectiveID());
      availablePerspectiveTitles.add(p.getPerspectiveTitle());
    }

    Set settingsIDs = m_settings.getSettingsIDs();
    for (String settingID : settingsIDs) {
      if (availablePerspectivesIDs.contains(settingID)) {
        int indexOfP = availablePerspectivesIDs.indexOf(settingID);

        // make a tab for this one
        Map settingsForID =
          m_settings.getSettings(settingID);
        if (settingsForID != null && settingsForID.size() > 0) {
          SingleSettingsEditor perpEditor =
            new SingleSettingsEditor(settingsForID);
          m_settingsTabs.addTab(availablePerspectiveTitles.get(indexOfP),
            perpEditor);
          m_perspectiveEditors.add(perpEditor);
        }
      }
    }
  }

  /**
   * Creates a single stand-alone settings editor panel
   *
   * @param settingsToEdit the settings to edit
   * @return a single settings editor panel for the supplied settings
   */
  public static SingleSettingsEditor createSingleSettingsEditor(
    Map settingsToEdit) {
    GenericObjectEditor.registerEditors();
    PropertyEditorManager.registerEditor(Color.class, ColorEditor.class);
    return new SingleSettingsEditor(settingsToEdit);
  }

  /**
   * Popup a single panel settings editor dialog for one group of related
   * settings
   *
   * @param settings the settings object containing (potentially) many groups of
   *          settings
   * @param settingsID the ID of the particular set of settings to edit
   * @param settingsName the name of this related set of settings
   * @param parent the parent window
   * @return the result chosen by the user (JOptionPane.OK_OPTION or
   *         JOptionPanel.CANCEL_OPTION)
   * @throws IOException if saving altered settings fails
   */
  public static int showSingleSettingsEditor(Settings settings,
    String settingsID, String settingsName, JComponent parent)
      throws IOException {
    return showSingleSettingsEditor(settings, settingsID, settingsName, parent,
      600, 300);
  }

  /**
   * Popup a single panel settings editor dialog for one group of related
   * settings
   * 
   * @param settings the settings object containing (potentially) many groups of
   *          settings
   * @param settingsID the ID of the particular set of settings to edit
   * @param settingsName the name of this related set of settings
   * @param parent the parent window
   * @param width the width for the dialog
   * @param height the height for the dialog
   * @return the result chosen by the user (JOptionPane.OK_OPTION or
   *         JOptionPanel.CANCEL_OPTION)
   * @throws IOException if saving altered settings fails
   */
  public static int showSingleSettingsEditor(Settings settings,
    String settingsID, String settingsName, JComponent parent, int width,
    int height) throws IOException {
    final SingleSettingsEditor sse =
      createSingleSettingsEditor(settings.getSettings(settingsID));
    sse.setPreferredSize(new Dimension(width, height));

    final JOptionPane pane = new JOptionPane(sse, JOptionPane.PLAIN_MESSAGE,
      JOptionPane.OK_CANCEL_OPTION);

    // There appears to be a bug in Java > 1.6 under Linux that, more often than
    // not, causes a sun.awt.X11.XException to occur when the following code
    // to make the dialog resizable is used. A workaround is to set the
    // suppressSwingDropSupport property to true (but this has to be done
    // at JVM startup, and setting it programatically, no matter how early,
    // does not seem to work). The hacky workaround here is to check for
    // a nix OS and disable the resizing, unless the user has specifically
    // used the -DsuppressSwingDropSupport=true JVM flag.
    //
    // See: http://bugs.java.com/view_bug.do?bug_id=7027598
    // and: http://mipav.cit.nih.gov/pubwiki/index.php/FAQ:_Why_do_I_get_an_exception_when_running_MIPAV_via_X11_forwarding_on_Linux%3F
    String os = System.getProperty("os.name").toLowerCase();
    String suppressSwingDropSupport =
      System.getProperty("suppressSwingDropSupport", "false");
    boolean nix = os.contains("nix") || os.contains("nux") || os.contains("aix");
    if (!nix || suppressSwingDropSupport.equalsIgnoreCase("true")) {
      pane.addHierarchyListener(new HierarchyListener() {
        @Override public void hierarchyChanged(HierarchyEvent e) {
          Window window = SwingUtilities.getWindowAncestor(pane);
          if (window instanceof Dialog) {
            Dialog dialog = (Dialog) window;
            if (!dialog.isResizable()) {
              dialog.setResizable(true);
            }
          }
        }
      });
    }
    JDialog dialog =
      pane.createDialog((JComponent) parent, settingsName + " Settings");
    dialog.show();
    Object resultO = pane.getValue();

    /*
     * int result = JOptionPane.showConfirmDialog(parent, sse, settingsName +
     * " Settings", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE,
     * wekaIcon);
     */

    int result = -1;
    if (resultO == null) {
      result = JOptionPane.CLOSED_OPTION;
    } else if (resultO instanceof Integer) {
      result = (Integer) resultO;
    }

    if (result == JOptionPane.OK_OPTION) {
      sse.applyToSettings();
      settings.saveSettings();
    }

    if (result == JOptionPane.OK_OPTION) {
      sse.applyToSettings();
      settings.saveSettings();
    }

    return result;
  }

  /**
   * Popup a settings editor for an application
   *
   * @param settings the settings object for the application
   * @param application the application itself
   * @return the result chosen by the user (JOptionPane.OK_OPTION or
   *         JOptionPanel.CANCEL)
   * @throws IOException if saving altered settings fails
   */
  public static int showApplicationSettingsEditor(Settings settings,
    GUIApplication application) throws IOException {

    final SettingsEditor settingsEditor =
      new SettingsEditor(settings, application);
    settingsEditor.setPreferredSize(new Dimension(800, 350));

    final JOptionPane pane = new JOptionPane(settingsEditor,
      JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);

    // There appears to be a bug in Java > 1.6 under Linux that, more often than
    // not, causes a sun.awt.X11.XException to occur when the following code
    // to make the dialog resizable is used. A workaround is to set the
    // suppressSwingDropSupport property to true (but this has to be done
    // at JVM startup, and setting it programatically, no matter how early,
    // does not seem to work). The hacky workaround here is to check for
    // a nix OS and disable the resizing, unless the user has specifically
    // used the -DsuppressSwingDropSupport=true JVM flag.
    //
    // See: http://bugs.java.com/view_bug.do?bug_id=7027598
    // and: http://mipav.cit.nih.gov/pubwiki/index.php/FAQ:_Why_do_I_get_an_exception_when_running_MIPAV_via_X11_forwarding_on_Linux%3F
    String os = System.getProperty("os.name").toLowerCase();
    String suppressSwingDropSupport =
      System.getProperty("suppressSwingDropSupport", "false");
    boolean nix = os.contains("nix") || os.contains("nux") || os.contains("aix");
    if (!nix || suppressSwingDropSupport.equalsIgnoreCase("true")) {
      pane.addHierarchyListener(new HierarchyListener() {
        @Override public void hierarchyChanged(HierarchyEvent e) {
          Window window = SwingUtilities.getWindowAncestor(pane);
          if (window instanceof Dialog) {
            Dialog dialog = (Dialog) window;
            if (!dialog.isResizable()) {
              dialog.setResizable(true);
            }
          }
        }
      });
    }
    JDialog dialog = pane.createDialog((JComponent) application,
      application.getApplicationName() + " Settings");
    dialog.show();
    Object resultO = pane.getValue();
    int result = -1;
    if (resultO == null) {
      result = JOptionPane.CLOSED_OPTION;
    } else if (resultO instanceof Integer) {
      result = (Integer) resultO;
    }

    if (result == JOptionPane.OK_OPTION) {
      settingsEditor.applyToSettings();
      settings.saveSettings();
    }

    return result;
  }

  public static class SingleSettingsEditor extends JPanel
    implements PropertyChangeListener {

    private static final long serialVersionUID = 8896265984902770239L;
    protected Map m_perspSettings;
    protected Map m_editorMap =
      new LinkedHashMap();

    public SingleSettingsEditor(Map pSettings) {
      m_perspSettings = pSettings;
      setLayout(new BorderLayout());

      JPanel scrollablePanel = new JPanel();
      JScrollPane scrollPane = new JScrollPane(scrollablePanel);
      scrollPane.setBorder(BorderFactory.createEmptyBorder());
      add(scrollPane, BorderLayout.CENTER);

      GridBagLayout gbLayout = new GridBagLayout();

      scrollablePanel.setLayout(gbLayout);
      setVisible(false);

      int i = 0;
      for (Map.Entry prop : pSettings.entrySet()) {
        Settings.SettingKey settingName = prop.getKey();
        if (settingName.getKey()
          .equals(PerspectiveManager.VISIBLE_PERSPECTIVES_KEY.getKey())) {
          // skip this as we've got a dedicated panel for this one
          continue;
        }
        Object settingValue = prop.getValue();
        List pickList = prop.getKey().getPickList();

        PropertyEditor editor = null;
        if (settingValue instanceof String && pickList != null
          && pickList.size() > 0) {
          // special case - list of legal values to choose from
          PickList pEditor = new PickList(pickList);
          pEditor.setValue(prop.getValue());
          editor = pEditor;
        }

        Class settingClass = settingValue.getClass();

        if (editor == null) {
          editor = PropertyEditorManager.findEditor(settingClass);
        }
        // hardcoded check for File here because we don't register File as the
        // FileEnvironmentField does not play nicely with the standard GOE
        // practice
        // of listening/updating for every key stroke on text fields
        if (settingValue instanceof java.io.File) {

          String dialogType = settingName.getMetadataElement(
            "java.io.File.dialogType", "" + JFileChooser.OPEN_DIALOG);
          String fileType =
            settingName.getMetadataElement("java.io.File.fileSelectionMode",
              "" + JFileChooser.FILES_AND_DIRECTORIES);

          int dType = Integer.parseInt(dialogType);
          int fType = Integer.parseInt(fileType);

          editor = new FileEnvironmentField("", Environment.getSystemWide(),
            dType, fType == JFileChooser.DIRECTORIES_ONLY);
        }

        if (editor == null) {
          // TODO check for primitive classes (need to do this it seems to be
          // backward
          // compatible with Java 1.6 - PropertyEditorManager only has built in
          // editors
          // for primitive int, float etc.)
          if (settingValue instanceof Integer) {
            settingClass = Integer.TYPE;
          } else if (settingValue instanceof Float) {
            settingClass = Float.TYPE;
          } else if (settingValue instanceof Double) {
            settingClass = Double.TYPE;
          } else if (settingValue instanceof Boolean) {
            settingClass = Boolean.TYPE;
          } else if (settingValue instanceof Long) {
            settingClass = Long.TYPE;
          } else if (settingValue instanceof Short) {
            settingClass = Short.TYPE;
          } else if (settingValue instanceof Byte) {
            settingClass = Byte.TYPE;
          }

          // TODO need an editor for enums under Java 1.6

          // try again
          editor = PropertyEditorManager.findEditor(settingClass);
        }

        // Check a few special cases - where the following
        // interfaces followed by superclass approach might get
        // fooled (i.e. classifiers that implement multiple interfaces
        // that themselves have entries in GUIEditors.props)
        if (editor == null) {
          if (settingValue instanceof Classifier) {
            editor = PropertyEditorManager.findEditor(Classifier.class);
            settingClass = Classifier.class;
          } else if (settingValue instanceof Clusterer) {
            editor = PropertyEditorManager.findEditor(Clusterer.class);
            settingClass = Clusterer.class;
          }
        }

        if (editor == null) {
          while (settingClass != null && editor == null) {
            // try interfaces first
            Class[] interfaces = settingClass.getInterfaces();
            if (interfaces != null) {
              for (Class intf : interfaces) {
                editor = PropertyEditorManager.findEditor(intf);
                if (editor != null) {
                  settingClass = intf;
                  break;
                }
              }
            }
            if (editor == null) {
              settingClass = settingClass.getSuperclass();
              if (settingClass != null) {
                editor = PropertyEditorManager.findEditor(settingClass);
              }
            }
          }
        }

        if (editor != null) {
          if (editor instanceof GenericObjectEditor) {
            ((GenericObjectEditor) editor).setClassType(settingClass);
          }
          editor.addPropertyChangeListener(this);
          editor.setValue(settingValue);
          JComponent view = null;

          if (editor.isPaintable() && editor.supportsCustomEditor()) {
            view = new PropertyPanel(editor);
          } else if (editor.supportsCustomEditor()
            && (editor.getCustomEditor() instanceof JComponent)) {
            view = (JComponent) editor.getCustomEditor();
          } else if (editor.getTags() != null) {
            view = new PropertyValueSelector(editor);
          } else if (editor.getAsText() != null) {
            view = new PropertyText(editor);
          } else {
            System.err.println("Warning: Property \"" + settingName
              + "\" has non-displayabale editor.  Skipping.");
            continue;
          }

          m_editorMap.put(settingName, editor);

          JLabel propLabel =
            new JLabel(settingName.getDescription(), SwingConstants.RIGHT);
          if (prop.getKey().getToolTip().length() > 0) {
            propLabel.setToolTipText(prop.getKey().getToolTip());
            view.setToolTipText(prop.getKey().getToolTip());
          }
          propLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 5));
          GridBagConstraints gbConstraints = new GridBagConstraints();
          gbConstraints.anchor = GridBagConstraints.EAST;
          gbConstraints.fill = GridBagConstraints.HORIZONTAL;
          gbConstraints.gridy = i;
          gbConstraints.gridx = 0;
          gbLayout.setConstraints(propLabel, gbConstraints);
          scrollablePanel.add(propLabel);

          JPanel newPanel = new JPanel();
          newPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 0, 10));
          newPanel.setLayout(new BorderLayout());
          newPanel.add(view, BorderLayout.CENTER);
          gbConstraints = new GridBagConstraints();
          gbConstraints.anchor = GridBagConstraints.WEST;
          gbConstraints.fill = GridBagConstraints.BOTH;
          gbConstraints.gridy = i;
          gbConstraints.gridx = 1;
          gbConstraints.weightx = 100;
          gbLayout.setConstraints(newPanel, gbConstraints);
          scrollablePanel.add(newPanel);

          i++;
        } else {
          System.err.println("SettingsEditor can't find an editor for: "
            + settingClass.toString());
        }
      }

      Dimension dim = scrollablePanel.getPreferredSize();
      dim.height += 20;
      dim.width += 20;
      scrollPane.setPreferredSize(dim);
      validate();

      setVisible(true);
    }

    public void applyToSettings() {
      for (Map.Entry e : m_perspSettings
        .entrySet()) {
        Settings.SettingKey settingKey = e.getKey();
        if (settingKey.getKey()
          .equals(PerspectiveManager.VISIBLE_PERSPECTIVES_KEY.getKey())) {
          continue;
        }
        PropertyEditor editor = m_editorMap.get(settingKey);
        if (editor != null) {
          Object newSettingValue = editor.getValue();
          m_perspSettings.put(settingKey, newSettingValue);
        }
      }
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
      repaint();
      revalidate();
    }
  }

  protected class PerspectiveSelector extends JPanel {

    private static final long serialVersionUID = -4765015948030757897L;
    protected List m_perspectiveChecks = new ArrayList();
    protected JCheckBox m_toolBarVisibleOnStartup =
      new JCheckBox("Perspective toolbar visible on start up");

    public PerspectiveSelector() {
      setLayout(new BorderLayout());

      List availablePerspectives =
        m_ownerApp.getPerspectiveManager().getLoadedPerspectives();
      if (availablePerspectives.size() > 0) {
        PerspectiveManager.SelectedPerspectivePreferences userSelected =
          new PerspectiveManager.SelectedPerspectivePreferences();
        userSelected = m_settings.getSetting(m_ownerApp.getApplicationID(),
          PerspectiveManager.VISIBLE_PERSPECTIVES_KEY, userSelected,
          Environment.getSystemWide());

        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
        if (!userSelected.getPerspectivesToolbarAlwaysHidden()) {
          p.add(m_toolBarVisibleOnStartup);
          m_toolBarVisibleOnStartup
            .setSelected(userSelected.getPerspectivesToolbarVisibleOnStartup());
        }

        for (Perspective perspective : availablePerspectives) {
          String pName = perspective.getPerspectiveTitle();
          JCheckBox jb = new JCheckBox(pName);
          jb.setSelected(
            userSelected.getUserVisiblePerspectives().contains(pName));
          m_perspectiveChecks.add(jb);
          p.add(jb);
        }

        add(p, BorderLayout.CENTER);
      }
    }

    public void applyToSettings() {
      LinkedList selectedPerspectives = new LinkedList();
      for (JCheckBox c : m_perspectiveChecks) {
        if (c.isSelected()) {
          selectedPerspectives.add(c.getText());
        }
      }
      PerspectiveManager.SelectedPerspectivePreferences newPrefs =
        new PerspectiveManager.SelectedPerspectivePreferences();
      newPrefs.setUserVisiblePerspectives(selectedPerspectives);
      newPrefs.setPerspectivesToolbarVisibleOnStartup(
        m_toolBarVisibleOnStartup.isSelected());
      m_settings.setSetting(m_ownerApp.getApplicationID(),
        PerspectiveManager.VISIBLE_PERSPECTIVES_KEY, newPrefs);
    }
  }

  /**
   * Simple property editor for pick lists
   */
  protected static class PickList extends JPanel implements PropertyEditor {

    private static final long serialVersionUID = 3505647427533464230L;
    protected JComboBox m_list = new JComboBox();

    public PickList(List list) {
      for (String item : list) {
        m_list.addItem(item);
      }

      setLayout(new BorderLayout());
      add(m_list, BorderLayout.CENTER);
    }

    @Override
    public void setValue(Object value) {
      m_list.setSelectedItem(value.toString());
    }

    @Override
    public Object getValue() {
      return m_list.getSelectedItem();
    }

    @Override
    public boolean isPaintable() {
      return false;
    }

    @Override
    public void paintValue(Graphics gfx, Rectangle box) {

    }

    @Override
    public String getJavaInitializationString() {
      return null;
    }

    @Override
    public String getAsText() {
      return null;
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {

    }

    @Override
    public String[] getTags() {
      return null;
    }

    @Override
    public Component getCustomEditor() {
      return this;
    }

    @Override
    public boolean supportsCustomEditor() {
      return true;
    }

    @Override
    public void addPropertyChangeListener(PropertyChangeListener listener) {

    }

    @Override
    public void removePropertyChangeListener(PropertyChangeListener listener) {

    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy